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 {
271 param_types,
272 return_type,
273 } => {
274 let params: Vec<String> =
275 param_types.iter().map(Self::map_hir_type_to_rust).collect();
276 let params_str = params.join(", ");
277 if matches!(**return_type, HirType::Void) {
278 format!("fn({})", params_str)
279 } else {
280 format!(
281 "fn({}) -> {}",
282 params_str,
283 Self::map_hir_type_to_rust(return_type)
284 )
285 }
286 }
287 HirType::StringLiteral => "&str".to_string(),
288 HirType::OwnedString => "String".to_string(),
289 HirType::StringReference => "&str".to_string(),
290 HirType::TypeAlias(name) => name.clone(),
292 }
293 }
294}
295
296impl Default for EnumGenerator {
297 fn default() -> Self {
298 Self::new()
299 }
300}
301
302#[cfg(test)]
303mod tests {
304 use super::*;
305 use decy_analyzer::tagged_union_analysis::{TaggedUnionInfo, VariantInfo};
306
307 #[test]
312 fn test_enum_generator_new() {
313 let gen = EnumGenerator::new();
314 assert!(std::mem::size_of_val(&gen) == 0);
316 }
317
318 #[test]
319 fn test_enum_generator_default() {
320 let gen: EnumGenerator = Default::default();
321 assert!(std::mem::size_of_val(&gen) == 0);
322 }
323
324 #[test]
329 fn test_generate_enum_single_variant() {
330 let gen = EnumGenerator::new();
331 let info = TaggedUnionInfo {
332 struct_name: "Value".to_string(),
333 tag_field_name: "tag".to_string(),
334 union_field_name: "data".to_string(),
335 variants: vec![VariantInfo {
336 name: "int_value".to_string(),
337 payload_type: HirType::Int,
338 }],
339 };
340
341 let result = gen.generate_enum(&info);
342 assert!(result.contains("#[derive(Debug, Clone, PartialEq)]"));
343 assert!(result.contains("pub enum Value"));
344 assert!(result.contains("IntValue(i32)"));
345 }
346
347 #[test]
348 fn test_generate_enum_multiple_variants() {
349 let gen = EnumGenerator::new();
350 let info = TaggedUnionInfo {
351 struct_name: "Value".to_string(),
352 tag_field_name: "tag".to_string(),
353 union_field_name: "data".to_string(),
354 variants: vec![
355 VariantInfo {
356 name: "int_value".to_string(),
357 payload_type: HirType::Int,
358 },
359 VariantInfo {
360 name: "float_value".to_string(),
361 payload_type: HirType::Float,
362 },
363 VariantInfo {
364 name: "double_value".to_string(),
365 payload_type: HirType::Double,
366 },
367 ],
368 };
369
370 let result = gen.generate_enum(&info);
371 assert!(result.contains("IntValue(i32),"));
372 assert!(result.contains("FloatValue(f32),"));
373 assert!(result.contains("DoubleValue(f64)"));
374 }
375
376 #[test]
377 fn test_generate_enum_void_variant() {
378 let gen = EnumGenerator::new();
379 let info = TaggedUnionInfo {
380 struct_name: "Option".to_string(),
381 tag_field_name: "tag".to_string(),
382 union_field_name: "data".to_string(),
383 variants: vec![
384 VariantInfo {
385 name: "none".to_string(),
386 payload_type: HirType::Void,
387 },
388 VariantInfo {
389 name: "some_int".to_string(),
390 payload_type: HirType::Int,
391 },
392 ],
393 };
394
395 let result = gen.generate_enum(&info);
396 assert!(result.contains("None,"));
398 assert!(result.contains("SomeInt(i32)"));
399 }
400
401 #[test]
402 fn test_generate_enum_last_variant_no_trailing_comma() {
403 let gen = EnumGenerator::new();
404 let info = TaggedUnionInfo {
405 struct_name: "Test".to_string(),
406 tag_field_name: "tag".to_string(),
407 union_field_name: "data".to_string(),
408 variants: vec![VariantInfo {
409 name: "value".to_string(),
410 payload_type: HirType::Int,
411 }],
412 };
413
414 let result = gen.generate_enum(&info);
415 assert!(result.contains("Value(i32)\n}"));
417 }
418
419 #[test]
424 fn test_capitalize_variant_name_snake_case() {
425 let result = EnumGenerator::capitalize_variant_name("int_value", &HirType::Int);
426 assert_eq!(result, "IntValue");
427 }
428
429 #[test]
430 fn test_capitalize_variant_name_short_name_uses_type() {
431 let result = EnumGenerator::capitalize_variant_name("i", &HirType::Int);
433 assert_eq!(result, "Int");
434
435 let result = EnumGenerator::capitalize_variant_name("f", &HirType::Float);
436 assert_eq!(result, "Float");
437
438 let result = EnumGenerator::capitalize_variant_name("d", &HirType::Double);
439 assert_eq!(result, "Double");
440 }
441
442 #[test]
443 fn test_capitalize_variant_name_two_char_name() {
444 let result = EnumGenerator::capitalize_variant_name("id", &HirType::Int);
445 assert_eq!(result, "Int"); }
447
448 #[test]
449 fn test_capitalize_variant_name_three_char_name() {
450 let result = EnumGenerator::capitalize_variant_name("val", &HirType::Int);
451 assert_eq!(result, "Val"); }
453
454 #[test]
455 fn test_capitalize_variant_name_empty_parts_fallback() {
456 let result = EnumGenerator::capitalize_variant_name("___", &HirType::Int);
458 assert_eq!(result, "Int");
459 }
460
461 #[test]
462 fn test_capitalize_variant_name_single_word() {
463 let result = EnumGenerator::capitalize_variant_name("value", &HirType::Int);
464 assert_eq!(result, "Value");
465 }
466
467 #[test]
468 fn test_capitalize_variant_name_multiple_underscores() {
469 let result = EnumGenerator::capitalize_variant_name("my__long__name", &HirType::Int);
470 assert_eq!(result, "MyLongName");
471 }
472
473 #[test]
478 fn test_type_based_variant_name_primitives() {
479 assert_eq!(
480 EnumGenerator::type_based_variant_name(&HirType::Void),
481 "None"
482 );
483 assert_eq!(EnumGenerator::type_based_variant_name(&HirType::Int), "Int");
484 assert_eq!(
485 EnumGenerator::type_based_variant_name(&HirType::UnsignedInt),
486 "UInt"
487 );
488 assert_eq!(
489 EnumGenerator::type_based_variant_name(&HirType::Float),
490 "Float"
491 );
492 assert_eq!(
493 EnumGenerator::type_based_variant_name(&HirType::Double),
494 "Double"
495 );
496 assert_eq!(
497 EnumGenerator::type_based_variant_name(&HirType::Char),
498 "Char"
499 );
500 }
501
502 #[test]
503 fn test_type_based_variant_name_char_pointer_is_string() {
504 let char_ptr = HirType::Pointer(Box::new(HirType::Char));
505 assert_eq!(EnumGenerator::type_based_variant_name(&char_ptr), "String");
506 }
507
508 #[test]
509 fn test_type_based_variant_name_void_pointer() {
510 let void_ptr = HirType::Pointer(Box::new(HirType::Void));
511 assert_eq!(EnumGenerator::type_based_variant_name(&void_ptr), "Pointer");
512 }
513
514 #[test]
515 fn test_type_based_variant_name_other_pointer() {
516 let int_ptr = HirType::Pointer(Box::new(HirType::Int));
517 assert_eq!(EnumGenerator::type_based_variant_name(&int_ptr), "Pointer");
518 }
519
520 #[test]
521 fn test_type_based_variant_name_box() {
522 let boxed = HirType::Box(Box::new(HirType::Int));
523 assert_eq!(EnumGenerator::type_based_variant_name(&boxed), "Boxed");
524 }
525
526 #[test]
527 fn test_type_based_variant_name_vec() {
528 let vec = HirType::Vec(Box::new(HirType::Int));
529 assert_eq!(EnumGenerator::type_based_variant_name(&vec), "Vec");
530 }
531
532 #[test]
533 fn test_type_based_variant_name_option() {
534 let opt = HirType::Option(Box::new(HirType::Int));
535 assert_eq!(EnumGenerator::type_based_variant_name(&opt), "Option");
536 }
537
538 #[test]
539 fn test_type_based_variant_name_reference() {
540 let ref_type = HirType::Reference {
541 inner: Box::new(HirType::Int),
542 mutable: false,
543 };
544 assert_eq!(EnumGenerator::type_based_variant_name(&ref_type), "Ref");
545 }
546
547 #[test]
548 fn test_type_based_variant_name_struct() {
549 let struct_type = HirType::Struct("MyStruct".to_string());
550 assert_eq!(
551 EnumGenerator::type_based_variant_name(&struct_type),
552 "MyStruct"
553 );
554 }
555
556 #[test]
557 fn test_type_based_variant_name_enum() {
558 let enum_type = HirType::Enum("MyEnum".to_string());
559 assert_eq!(EnumGenerator::type_based_variant_name(&enum_type), "MyEnum");
560 }
561
562 #[test]
563 fn test_type_based_variant_name_union() {
564 let union_type = HirType::Union(vec![]);
565 assert_eq!(EnumGenerator::type_based_variant_name(&union_type), "Union");
566 }
567
568 #[test]
569 fn test_type_based_variant_name_array() {
570 let array = HirType::Array {
571 element_type: Box::new(HirType::Int),
572 size: Some(10),
573 };
574 assert_eq!(EnumGenerator::type_based_variant_name(&array), "Array");
575 }
576
577 #[test]
578 fn test_type_based_variant_name_function_pointer() {
579 let fn_ptr = HirType::FunctionPointer {
580 param_types: vec![HirType::Int],
581 return_type: Box::new(HirType::Void),
582 };
583 assert_eq!(EnumGenerator::type_based_variant_name(&fn_ptr), "Function");
584 }
585
586 #[test]
587 fn test_type_based_variant_name_string_types() {
588 assert_eq!(
589 EnumGenerator::type_based_variant_name(&HirType::StringLiteral),
590 "String"
591 );
592 assert_eq!(
593 EnumGenerator::type_based_variant_name(&HirType::OwnedString),
594 "String"
595 );
596 assert_eq!(
597 EnumGenerator::type_based_variant_name(&HirType::StringReference),
598 "String"
599 );
600 }
601
602 #[test]
603 fn test_type_based_variant_name_type_alias() {
604 let alias = HirType::TypeAlias("size_t".to_string());
605 assert_eq!(EnumGenerator::type_based_variant_name(&alias), "size_t");
606 }
607
608 #[test]
613 fn test_map_hir_type_primitives() {
614 assert_eq!(EnumGenerator::map_hir_type_to_rust(&HirType::Void), "()");
615 assert_eq!(EnumGenerator::map_hir_type_to_rust(&HirType::Int), "i32");
616 assert_eq!(
617 EnumGenerator::map_hir_type_to_rust(&HirType::UnsignedInt),
618 "u32"
619 );
620 assert_eq!(EnumGenerator::map_hir_type_to_rust(&HirType::Float), "f32");
621 assert_eq!(EnumGenerator::map_hir_type_to_rust(&HirType::Double), "f64");
622 assert_eq!(EnumGenerator::map_hir_type_to_rust(&HirType::Char), "u8");
623 }
624
625 #[test]
626 fn test_map_hir_type_char_pointer_to_string() {
627 let char_ptr = HirType::Pointer(Box::new(HirType::Char));
628 assert_eq!(EnumGenerator::map_hir_type_to_rust(&char_ptr), "String");
629 }
630
631 #[test]
632 fn test_map_hir_type_void_pointer() {
633 let void_ptr = HirType::Pointer(Box::new(HirType::Void));
634 assert_eq!(EnumGenerator::map_hir_type_to_rust(&void_ptr), "*mut ()");
635 }
636
637 #[test]
638 fn test_map_hir_type_other_pointer() {
639 let int_ptr = HirType::Pointer(Box::new(HirType::Int));
640 assert_eq!(EnumGenerator::map_hir_type_to_rust(&int_ptr), "*mut i32");
641 }
642
643 #[test]
644 fn test_map_hir_type_box() {
645 let boxed = HirType::Box(Box::new(HirType::Int));
646 assert_eq!(EnumGenerator::map_hir_type_to_rust(&boxed), "Box<i32>");
647 }
648
649 #[test]
650 fn test_map_hir_type_vec() {
651 let vec = HirType::Vec(Box::new(HirType::Float));
652 assert_eq!(EnumGenerator::map_hir_type_to_rust(&vec), "Vec<f32>");
653 }
654
655 #[test]
656 fn test_map_hir_type_option() {
657 let opt = HirType::Option(Box::new(HirType::Double));
658 assert_eq!(EnumGenerator::map_hir_type_to_rust(&opt), "Option<f64>");
659 }
660
661 #[test]
662 fn test_map_hir_type_immutable_reference() {
663 let ref_type = HirType::Reference {
664 inner: Box::new(HirType::Int),
665 mutable: false,
666 };
667 assert_eq!(EnumGenerator::map_hir_type_to_rust(&ref_type), "&i32");
668 }
669
670 #[test]
671 fn test_map_hir_type_mutable_reference() {
672 let ref_type = HirType::Reference {
673 inner: Box::new(HirType::Int),
674 mutable: true,
675 };
676 assert_eq!(EnumGenerator::map_hir_type_to_rust(&ref_type), "&mut i32");
677 }
678
679 #[test]
680 fn test_map_hir_type_struct() {
681 let struct_type = HirType::Struct("Point".to_string());
682 assert_eq!(EnumGenerator::map_hir_type_to_rust(&struct_type), "Point");
683 }
684
685 #[test]
686 fn test_map_hir_type_enum() {
687 let enum_type = HirType::Enum("Color".to_string());
688 assert_eq!(EnumGenerator::map_hir_type_to_rust(&enum_type), "Color");
689 }
690
691 #[test]
692 fn test_map_hir_type_union() {
693 let union_type = HirType::Union(vec![]);
694 assert_eq!(
695 EnumGenerator::map_hir_type_to_rust(&union_type),
696 "/* Union */"
697 );
698 }
699
700 #[test]
701 fn test_map_hir_type_array_with_size() {
702 let array = HirType::Array {
703 element_type: Box::new(HirType::Int),
704 size: Some(10),
705 };
706 assert_eq!(EnumGenerator::map_hir_type_to_rust(&array), "[i32; 10]");
707 }
708
709 #[test]
710 fn test_map_hir_type_array_without_size() {
711 let array = HirType::Array {
712 element_type: Box::new(HirType::Int),
713 size: None,
714 };
715 assert_eq!(EnumGenerator::map_hir_type_to_rust(&array), "[i32]");
716 }
717
718 #[test]
719 fn test_map_hir_type_function_pointer_void_return() {
720 let fn_ptr = HirType::FunctionPointer {
721 param_types: vec![HirType::Int, HirType::Float],
722 return_type: Box::new(HirType::Void),
723 };
724 assert_eq!(EnumGenerator::map_hir_type_to_rust(&fn_ptr), "fn(i32, f32)");
725 }
726
727 #[test]
728 fn test_map_hir_type_function_pointer_with_return() {
729 let fn_ptr = HirType::FunctionPointer {
730 param_types: vec![HirType::Int],
731 return_type: Box::new(HirType::Int),
732 };
733 assert_eq!(
734 EnumGenerator::map_hir_type_to_rust(&fn_ptr),
735 "fn(i32) -> i32"
736 );
737 }
738
739 #[test]
740 fn test_map_hir_type_function_pointer_no_params() {
741 let fn_ptr = HirType::FunctionPointer {
742 param_types: vec![],
743 return_type: Box::new(HirType::Int),
744 };
745 assert_eq!(EnumGenerator::map_hir_type_to_rust(&fn_ptr), "fn() -> i32");
746 }
747
748 #[test]
749 fn test_map_hir_type_string_types() {
750 assert_eq!(
751 EnumGenerator::map_hir_type_to_rust(&HirType::StringLiteral),
752 "&str"
753 );
754 assert_eq!(
755 EnumGenerator::map_hir_type_to_rust(&HirType::OwnedString),
756 "String"
757 );
758 assert_eq!(
759 EnumGenerator::map_hir_type_to_rust(&HirType::StringReference),
760 "&str"
761 );
762 }
763
764 #[test]
765 fn test_map_hir_type_type_alias() {
766 let alias = HirType::TypeAlias("size_t".to_string());
767 assert_eq!(EnumGenerator::map_hir_type_to_rust(&alias), "size_t");
768 }
769
770 #[test]
775 fn test_generate_enum_complete_value_type() {
776 let gen = EnumGenerator::new();
777 let info = TaggedUnionInfo {
778 struct_name: "Value".to_string(),
779 tag_field_name: "type".to_string(),
780 union_field_name: "data".to_string(),
781 variants: vec![
782 VariantInfo {
783 name: "i".to_string(), payload_type: HirType::Int,
785 },
786 VariantInfo {
787 name: "f".to_string(), payload_type: HirType::Float,
789 },
790 VariantInfo {
791 name: "s".to_string(), payload_type: HirType::Pointer(Box::new(HirType::Char)),
793 },
794 ],
795 };
796
797 let result = gen.generate_enum(&info);
798
799 assert!(result.contains("#[derive(Debug, Clone, PartialEq)]"));
801
802 assert!(result.contains("pub enum Value {"));
804
805 assert!(result.contains("Int(i32)"));
807 assert!(result.contains("Float(f32)"));
808 assert!(result.contains("String(String)")); }
810
811 #[test]
812 fn test_generate_enum_with_nested_types() {
813 let gen = EnumGenerator::new();
814 let info = TaggedUnionInfo {
815 struct_name: "Container".to_string(),
816 tag_field_name: "kind".to_string(),
817 union_field_name: "data".to_string(),
818 variants: vec![
819 VariantInfo {
820 name: "boxed_int".to_string(),
821 payload_type: HirType::Box(Box::new(HirType::Int)),
822 },
823 VariantInfo {
824 name: "vec_float".to_string(),
825 payload_type: HirType::Vec(Box::new(HirType::Float)),
826 },
827 ],
828 };
829
830 let result = gen.generate_enum(&info);
831 assert!(result.contains("BoxedInt(Box<i32>)"));
832 assert!(result.contains("VecFloat(Vec<f32>)"));
833 }
834}