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::Int => "Int".to_string(),
203 HirType::UnsignedInt => "UInt".to_string(), HirType::Float => "Float".to_string(),
205 HirType::Double => "Double".to_string(),
206 HirType::Char => "Char".to_string(),
207 HirType::Pointer(inner) if matches!(**inner, HirType::Char) => "String".to_string(),
208 HirType::Pointer(inner) if matches!(**inner, HirType::Void) => "Pointer".to_string(),
209 HirType::Pointer(_) => "Pointer".to_string(),
210 HirType::Box(_) => "Boxed".to_string(),
211 HirType::Vec(_) => "Vec".to_string(),
212 HirType::Option(_) => "Option".to_string(),
213 HirType::Reference { .. } => "Ref".to_string(),
214 HirType::Struct(name) => name.clone(),
215 HirType::Enum(name) => name.clone(),
216 HirType::Union(_) => "Union".to_string(),
217 HirType::Array { .. } => "Array".to_string(),
218 HirType::FunctionPointer { .. } => "Function".to_string(),
219 HirType::StringLiteral | HirType::OwnedString | HirType::StringReference => {
220 "String".to_string()
221 }
222 HirType::TypeAlias(name) => name.clone(),
224 }
225 }
226
227 fn map_hir_type_to_rust(hir_type: &HirType) -> String {
229 match hir_type {
230 HirType::Void => "()".to_string(),
231 HirType::Int => "i32".to_string(),
232 HirType::UnsignedInt => "u32".to_string(), HirType::Float => "f32".to_string(),
234 HirType::Double => "f64".to_string(),
235 HirType::Char => "u8".to_string(),
236 HirType::Pointer(inner) => {
237 if matches!(**inner, HirType::Char) {
239 "String".to_string()
240 } else if matches!(**inner, HirType::Void) {
241 "*mut ()".to_string()
242 } else {
243 format!("*mut {}", Self::map_hir_type_to_rust(inner))
244 }
245 }
246 HirType::Box(inner) => format!("Box<{}>", Self::map_hir_type_to_rust(inner)),
247 HirType::Vec(inner) => format!("Vec<{}>", Self::map_hir_type_to_rust(inner)),
248 HirType::Option(inner) => format!("Option<{}>", Self::map_hir_type_to_rust(inner)),
249 HirType::Reference { inner, mutable } => {
250 if *mutable {
251 format!("&mut {}", Self::map_hir_type_to_rust(inner))
252 } else {
253 format!("&{}", Self::map_hir_type_to_rust(inner))
254 }
255 }
256 HirType::Struct(name) => name.clone(),
257 HirType::Enum(name) => name.clone(),
258 HirType::Union(_) => "/* Union */".to_string(),
259 HirType::Array { element_type, size } => {
260 if let Some(n) = size {
261 format!("[{}; {}]", Self::map_hir_type_to_rust(element_type), n)
262 } else {
263 format!("[{}]", Self::map_hir_type_to_rust(element_type))
264 }
265 }
266 HirType::FunctionPointer {
267 param_types,
268 return_type,
269 } => {
270 let params: Vec<String> =
271 param_types.iter().map(Self::map_hir_type_to_rust).collect();
272 let params_str = params.join(", ");
273 if matches!(**return_type, HirType::Void) {
274 format!("fn({})", params_str)
275 } else {
276 format!(
277 "fn({}) -> {}",
278 params_str,
279 Self::map_hir_type_to_rust(return_type)
280 )
281 }
282 }
283 HirType::StringLiteral => "&str".to_string(),
284 HirType::OwnedString => "String".to_string(),
285 HirType::StringReference => "&str".to_string(),
286 HirType::TypeAlias(name) => name.clone(),
288 }
289 }
290}
291
292impl Default for EnumGenerator {
293 fn default() -> Self {
294 Self::new()
295 }
296}
297
298#[cfg(test)]
299mod tests {
300 use super::*;
301 use decy_analyzer::tagged_union_analysis::{TaggedUnionInfo, VariantInfo};
302
303 #[test]
308 fn test_enum_generator_new() {
309 let gen = EnumGenerator::new();
310 assert!(std::mem::size_of_val(&gen) == 0);
312 }
313
314 #[test]
315 fn test_enum_generator_default() {
316 let gen: EnumGenerator = Default::default();
317 assert!(std::mem::size_of_val(&gen) == 0);
318 }
319
320 #[test]
325 fn test_generate_enum_single_variant() {
326 let gen = EnumGenerator::new();
327 let info = TaggedUnionInfo {
328 struct_name: "Value".to_string(),
329 tag_field_name: "tag".to_string(),
330 union_field_name: "data".to_string(),
331 variants: vec![VariantInfo {
332 name: "int_value".to_string(),
333 payload_type: HirType::Int,
334 }],
335 };
336
337 let result = gen.generate_enum(&info);
338 assert!(result.contains("#[derive(Debug, Clone, PartialEq)]"));
339 assert!(result.contains("pub enum Value"));
340 assert!(result.contains("IntValue(i32)"));
341 }
342
343 #[test]
344 fn test_generate_enum_multiple_variants() {
345 let gen = EnumGenerator::new();
346 let info = TaggedUnionInfo {
347 struct_name: "Value".to_string(),
348 tag_field_name: "tag".to_string(),
349 union_field_name: "data".to_string(),
350 variants: vec![
351 VariantInfo {
352 name: "int_value".to_string(),
353 payload_type: HirType::Int,
354 },
355 VariantInfo {
356 name: "float_value".to_string(),
357 payload_type: HirType::Float,
358 },
359 VariantInfo {
360 name: "double_value".to_string(),
361 payload_type: HirType::Double,
362 },
363 ],
364 };
365
366 let result = gen.generate_enum(&info);
367 assert!(result.contains("IntValue(i32),"));
368 assert!(result.contains("FloatValue(f32),"));
369 assert!(result.contains("DoubleValue(f64)"));
370 }
371
372 #[test]
373 fn test_generate_enum_void_variant() {
374 let gen = EnumGenerator::new();
375 let info = TaggedUnionInfo {
376 struct_name: "Option".to_string(),
377 tag_field_name: "tag".to_string(),
378 union_field_name: "data".to_string(),
379 variants: vec![
380 VariantInfo {
381 name: "none".to_string(),
382 payload_type: HirType::Void,
383 },
384 VariantInfo {
385 name: "some_int".to_string(),
386 payload_type: HirType::Int,
387 },
388 ],
389 };
390
391 let result = gen.generate_enum(&info);
392 assert!(result.contains("None,"));
394 assert!(result.contains("SomeInt(i32)"));
395 }
396
397 #[test]
398 fn test_generate_enum_last_variant_no_trailing_comma() {
399 let gen = EnumGenerator::new();
400 let info = TaggedUnionInfo {
401 struct_name: "Test".to_string(),
402 tag_field_name: "tag".to_string(),
403 union_field_name: "data".to_string(),
404 variants: vec![VariantInfo {
405 name: "value".to_string(),
406 payload_type: HirType::Int,
407 }],
408 };
409
410 let result = gen.generate_enum(&info);
411 assert!(result.contains("Value(i32)\n}"));
413 }
414
415 #[test]
420 fn test_capitalize_variant_name_snake_case() {
421 let result = EnumGenerator::capitalize_variant_name("int_value", &HirType::Int);
422 assert_eq!(result, "IntValue");
423 }
424
425 #[test]
426 fn test_capitalize_variant_name_short_name_uses_type() {
427 let result = EnumGenerator::capitalize_variant_name("i", &HirType::Int);
429 assert_eq!(result, "Int");
430
431 let result = EnumGenerator::capitalize_variant_name("f", &HirType::Float);
432 assert_eq!(result, "Float");
433
434 let result = EnumGenerator::capitalize_variant_name("d", &HirType::Double);
435 assert_eq!(result, "Double");
436 }
437
438 #[test]
439 fn test_capitalize_variant_name_two_char_name() {
440 let result = EnumGenerator::capitalize_variant_name("id", &HirType::Int);
441 assert_eq!(result, "Int"); }
443
444 #[test]
445 fn test_capitalize_variant_name_three_char_name() {
446 let result = EnumGenerator::capitalize_variant_name("val", &HirType::Int);
447 assert_eq!(result, "Val"); }
449
450 #[test]
451 fn test_capitalize_variant_name_empty_parts_fallback() {
452 let result = EnumGenerator::capitalize_variant_name("___", &HirType::Int);
454 assert_eq!(result, "Int");
455 }
456
457 #[test]
458 fn test_capitalize_variant_name_single_word() {
459 let result = EnumGenerator::capitalize_variant_name("value", &HirType::Int);
460 assert_eq!(result, "Value");
461 }
462
463 #[test]
464 fn test_capitalize_variant_name_multiple_underscores() {
465 let result = EnumGenerator::capitalize_variant_name("my__long__name", &HirType::Int);
466 assert_eq!(result, "MyLongName");
467 }
468
469 #[test]
474 fn test_type_based_variant_name_primitives() {
475 assert_eq!(EnumGenerator::type_based_variant_name(&HirType::Void), "None");
476 assert_eq!(EnumGenerator::type_based_variant_name(&HirType::Int), "Int");
477 assert_eq!(EnumGenerator::type_based_variant_name(&HirType::UnsignedInt), "UInt");
478 assert_eq!(EnumGenerator::type_based_variant_name(&HirType::Float), "Float");
479 assert_eq!(EnumGenerator::type_based_variant_name(&HirType::Double), "Double");
480 assert_eq!(EnumGenerator::type_based_variant_name(&HirType::Char), "Char");
481 }
482
483 #[test]
484 fn test_type_based_variant_name_char_pointer_is_string() {
485 let char_ptr = HirType::Pointer(Box::new(HirType::Char));
486 assert_eq!(EnumGenerator::type_based_variant_name(&char_ptr), "String");
487 }
488
489 #[test]
490 fn test_type_based_variant_name_void_pointer() {
491 let void_ptr = HirType::Pointer(Box::new(HirType::Void));
492 assert_eq!(EnumGenerator::type_based_variant_name(&void_ptr), "Pointer");
493 }
494
495 #[test]
496 fn test_type_based_variant_name_other_pointer() {
497 let int_ptr = HirType::Pointer(Box::new(HirType::Int));
498 assert_eq!(EnumGenerator::type_based_variant_name(&int_ptr), "Pointer");
499 }
500
501 #[test]
502 fn test_type_based_variant_name_box() {
503 let boxed = HirType::Box(Box::new(HirType::Int));
504 assert_eq!(EnumGenerator::type_based_variant_name(&boxed), "Boxed");
505 }
506
507 #[test]
508 fn test_type_based_variant_name_vec() {
509 let vec = HirType::Vec(Box::new(HirType::Int));
510 assert_eq!(EnumGenerator::type_based_variant_name(&vec), "Vec");
511 }
512
513 #[test]
514 fn test_type_based_variant_name_option() {
515 let opt = HirType::Option(Box::new(HirType::Int));
516 assert_eq!(EnumGenerator::type_based_variant_name(&opt), "Option");
517 }
518
519 #[test]
520 fn test_type_based_variant_name_reference() {
521 let ref_type = HirType::Reference {
522 inner: Box::new(HirType::Int),
523 mutable: false,
524 };
525 assert_eq!(EnumGenerator::type_based_variant_name(&ref_type), "Ref");
526 }
527
528 #[test]
529 fn test_type_based_variant_name_struct() {
530 let struct_type = HirType::Struct("MyStruct".to_string());
531 assert_eq!(EnumGenerator::type_based_variant_name(&struct_type), "MyStruct");
532 }
533
534 #[test]
535 fn test_type_based_variant_name_enum() {
536 let enum_type = HirType::Enum("MyEnum".to_string());
537 assert_eq!(EnumGenerator::type_based_variant_name(&enum_type), "MyEnum");
538 }
539
540 #[test]
541 fn test_type_based_variant_name_union() {
542 let union_type = HirType::Union(vec![]);
543 assert_eq!(EnumGenerator::type_based_variant_name(&union_type), "Union");
544 }
545
546 #[test]
547 fn test_type_based_variant_name_array() {
548 let array = HirType::Array {
549 element_type: Box::new(HirType::Int),
550 size: Some(10),
551 };
552 assert_eq!(EnumGenerator::type_based_variant_name(&array), "Array");
553 }
554
555 #[test]
556 fn test_type_based_variant_name_function_pointer() {
557 let fn_ptr = HirType::FunctionPointer {
558 param_types: vec![HirType::Int],
559 return_type: Box::new(HirType::Void),
560 };
561 assert_eq!(EnumGenerator::type_based_variant_name(&fn_ptr), "Function");
562 }
563
564 #[test]
565 fn test_type_based_variant_name_string_types() {
566 assert_eq!(
567 EnumGenerator::type_based_variant_name(&HirType::StringLiteral),
568 "String"
569 );
570 assert_eq!(
571 EnumGenerator::type_based_variant_name(&HirType::OwnedString),
572 "String"
573 );
574 assert_eq!(
575 EnumGenerator::type_based_variant_name(&HirType::StringReference),
576 "String"
577 );
578 }
579
580 #[test]
581 fn test_type_based_variant_name_type_alias() {
582 let alias = HirType::TypeAlias("size_t".to_string());
583 assert_eq!(EnumGenerator::type_based_variant_name(&alias), "size_t");
584 }
585
586 #[test]
591 fn test_map_hir_type_primitives() {
592 assert_eq!(EnumGenerator::map_hir_type_to_rust(&HirType::Void), "()");
593 assert_eq!(EnumGenerator::map_hir_type_to_rust(&HirType::Int), "i32");
594 assert_eq!(EnumGenerator::map_hir_type_to_rust(&HirType::UnsignedInt), "u32");
595 assert_eq!(EnumGenerator::map_hir_type_to_rust(&HirType::Float), "f32");
596 assert_eq!(EnumGenerator::map_hir_type_to_rust(&HirType::Double), "f64");
597 assert_eq!(EnumGenerator::map_hir_type_to_rust(&HirType::Char), "u8");
598 }
599
600 #[test]
601 fn test_map_hir_type_char_pointer_to_string() {
602 let char_ptr = HirType::Pointer(Box::new(HirType::Char));
603 assert_eq!(EnumGenerator::map_hir_type_to_rust(&char_ptr), "String");
604 }
605
606 #[test]
607 fn test_map_hir_type_void_pointer() {
608 let void_ptr = HirType::Pointer(Box::new(HirType::Void));
609 assert_eq!(EnumGenerator::map_hir_type_to_rust(&void_ptr), "*mut ()");
610 }
611
612 #[test]
613 fn test_map_hir_type_other_pointer() {
614 let int_ptr = HirType::Pointer(Box::new(HirType::Int));
615 assert_eq!(EnumGenerator::map_hir_type_to_rust(&int_ptr), "*mut i32");
616 }
617
618 #[test]
619 fn test_map_hir_type_box() {
620 let boxed = HirType::Box(Box::new(HirType::Int));
621 assert_eq!(EnumGenerator::map_hir_type_to_rust(&boxed), "Box<i32>");
622 }
623
624 #[test]
625 fn test_map_hir_type_vec() {
626 let vec = HirType::Vec(Box::new(HirType::Float));
627 assert_eq!(EnumGenerator::map_hir_type_to_rust(&vec), "Vec<f32>");
628 }
629
630 #[test]
631 fn test_map_hir_type_option() {
632 let opt = HirType::Option(Box::new(HirType::Double));
633 assert_eq!(EnumGenerator::map_hir_type_to_rust(&opt), "Option<f64>");
634 }
635
636 #[test]
637 fn test_map_hir_type_immutable_reference() {
638 let ref_type = HirType::Reference {
639 inner: Box::new(HirType::Int),
640 mutable: false,
641 };
642 assert_eq!(EnumGenerator::map_hir_type_to_rust(&ref_type), "&i32");
643 }
644
645 #[test]
646 fn test_map_hir_type_mutable_reference() {
647 let ref_type = HirType::Reference {
648 inner: Box::new(HirType::Int),
649 mutable: true,
650 };
651 assert_eq!(EnumGenerator::map_hir_type_to_rust(&ref_type), "&mut i32");
652 }
653
654 #[test]
655 fn test_map_hir_type_struct() {
656 let struct_type = HirType::Struct("Point".to_string());
657 assert_eq!(EnumGenerator::map_hir_type_to_rust(&struct_type), "Point");
658 }
659
660 #[test]
661 fn test_map_hir_type_enum() {
662 let enum_type = HirType::Enum("Color".to_string());
663 assert_eq!(EnumGenerator::map_hir_type_to_rust(&enum_type), "Color");
664 }
665
666 #[test]
667 fn test_map_hir_type_union() {
668 let union_type = HirType::Union(vec![]);
669 assert_eq!(EnumGenerator::map_hir_type_to_rust(&union_type), "/* Union */");
670 }
671
672 #[test]
673 fn test_map_hir_type_array_with_size() {
674 let array = HirType::Array {
675 element_type: Box::new(HirType::Int),
676 size: Some(10),
677 };
678 assert_eq!(EnumGenerator::map_hir_type_to_rust(&array), "[i32; 10]");
679 }
680
681 #[test]
682 fn test_map_hir_type_array_without_size() {
683 let array = HirType::Array {
684 element_type: Box::new(HirType::Int),
685 size: None,
686 };
687 assert_eq!(EnumGenerator::map_hir_type_to_rust(&array), "[i32]");
688 }
689
690 #[test]
691 fn test_map_hir_type_function_pointer_void_return() {
692 let fn_ptr = HirType::FunctionPointer {
693 param_types: vec![HirType::Int, HirType::Float],
694 return_type: Box::new(HirType::Void),
695 };
696 assert_eq!(EnumGenerator::map_hir_type_to_rust(&fn_ptr), "fn(i32, f32)");
697 }
698
699 #[test]
700 fn test_map_hir_type_function_pointer_with_return() {
701 let fn_ptr = HirType::FunctionPointer {
702 param_types: vec![HirType::Int],
703 return_type: Box::new(HirType::Int),
704 };
705 assert_eq!(EnumGenerator::map_hir_type_to_rust(&fn_ptr), "fn(i32) -> i32");
706 }
707
708 #[test]
709 fn test_map_hir_type_function_pointer_no_params() {
710 let fn_ptr = HirType::FunctionPointer {
711 param_types: vec![],
712 return_type: Box::new(HirType::Int),
713 };
714 assert_eq!(EnumGenerator::map_hir_type_to_rust(&fn_ptr), "fn() -> i32");
715 }
716
717 #[test]
718 fn test_map_hir_type_string_types() {
719 assert_eq!(EnumGenerator::map_hir_type_to_rust(&HirType::StringLiteral), "&str");
720 assert_eq!(EnumGenerator::map_hir_type_to_rust(&HirType::OwnedString), "String");
721 assert_eq!(EnumGenerator::map_hir_type_to_rust(&HirType::StringReference), "&str");
722 }
723
724 #[test]
725 fn test_map_hir_type_type_alias() {
726 let alias = HirType::TypeAlias("size_t".to_string());
727 assert_eq!(EnumGenerator::map_hir_type_to_rust(&alias), "size_t");
728 }
729
730 #[test]
735 fn test_generate_enum_complete_value_type() {
736 let gen = EnumGenerator::new();
737 let info = TaggedUnionInfo {
738 struct_name: "Value".to_string(),
739 tag_field_name: "type".to_string(),
740 union_field_name: "data".to_string(),
741 variants: vec![
742 VariantInfo {
743 name: "i".to_string(), payload_type: HirType::Int,
745 },
746 VariantInfo {
747 name: "f".to_string(), payload_type: HirType::Float,
749 },
750 VariantInfo {
751 name: "s".to_string(), payload_type: HirType::Pointer(Box::new(HirType::Char)),
753 },
754 ],
755 };
756
757 let result = gen.generate_enum(&info);
758
759 assert!(result.contains("#[derive(Debug, Clone, PartialEq)]"));
761
762 assert!(result.contains("pub enum Value {"));
764
765 assert!(result.contains("Int(i32)"));
767 assert!(result.contains("Float(f32)"));
768 assert!(result.contains("String(String)")); }
770
771 #[test]
772 fn test_generate_enum_with_nested_types() {
773 let gen = EnumGenerator::new();
774 let info = TaggedUnionInfo {
775 struct_name: "Container".to_string(),
776 tag_field_name: "kind".to_string(),
777 union_field_name: "data".to_string(),
778 variants: vec![
779 VariantInfo {
780 name: "boxed_int".to_string(),
781 payload_type: HirType::Box(Box::new(HirType::Int)),
782 },
783 VariantInfo {
784 name: "vec_float".to_string(),
785 payload_type: HirType::Vec(Box::new(HirType::Float)),
786 },
787 ],
788 };
789
790 let result = gen.generate_enum(&info);
791 assert!(result.contains("BoxedInt(Box<i32>)"));
792 assert!(result.contains("VecFloat(Vec<f32>)"));
793 }
794}