Skip to main content

decy_codegen/
enum_gen.rs

1//! Enum generation from tagged unions (DECY-081).
2//!
3//! Transforms C tagged unions into type-safe Rust enums with pattern matching.
4//!
5//! # Overview
6//!
7//! This module generates idiomatic Rust `enum` definitions from C tagged union patterns.
8//! It takes the tagged union metadata extracted by the analyzer and produces clean,
9//! type-safe Rust code that eliminates the unsafe union access pattern.
10//!
11//! # C Tagged Union Pattern
12//!
13//! C code often uses the "tagged union" pattern for variant types:
14//!
15//! ```c
16//! enum ValueType { TYPE_INT, TYPE_FLOAT, TYPE_STRING };
17//!
18//! struct Value {
19//!     enum ValueType tag;  // Discriminant
20//!     union {              // Payload
21//!         int int_value;
22//!         float float_value;
23//!         char* string_value;
24//!     } data;
25//! };
26//! ```
27//!
28//! This is unsafe because:
29//! - The compiler doesn't verify tag matches union field access
30//! - Reading wrong union field causes undefined behavior
31//! - No exhaustiveness checking for tag values
32//!
33//! # Rust Enum Transformation
34//!
35//! This module transforms the unsafe C pattern into safe Rust:
36//!
37//! ```rust
38//! #[derive(Debug, Clone, PartialEq)]
39//! pub enum Value {
40//!     Int(i32),
41//!     Float(f32),
42//!     String(String),
43//! }
44//! ```
45//!
46//! Benefits:
47//! - Type-safe: Compiler ensures tag matches payload
48//! - Exhaustive: Pattern matching requires all variants
49//! - Zero unsafe code in generated output
50//!
51//! # Example
52//!
53//! ```no_run
54//! use decy_analyzer::tagged_union_analysis::TaggedUnionAnalyzer;
55//! use decy_codegen::enum_gen::EnumGenerator;
56//! use decy_hir::{HirStruct, HirStructField, HirType};
57//!
58//! // C: struct Value { enum Tag tag; union { int i; float f; } data; };
59//! let struct_def = HirStruct::new(
60//!     "Value".to_string(),
61//!     vec![
62//!         HirStructField::new("tag".to_string(), HirType::Enum("Tag".to_string())),
63//!         HirStructField::new("data".to_string(), HirType::Union(vec![
64//!             ("i".to_string(), HirType::Int),
65//!             ("f".to_string(), HirType::Float),
66//!         ])),
67//!     ],
68//! );
69//!
70//! // Analyze tagged union
71//! let analyzer = TaggedUnionAnalyzer::new();
72//! let info = analyzer.analyze_struct(&struct_def).unwrap();
73//!
74//! // Generate Rust enum
75//! let generator = EnumGenerator::new();
76//! let rust_enum = generator.generate_enum(&info);
77//!
78//! // Result:
79//! // #[derive(Debug, Clone, PartialEq)]
80//! // pub enum Value {
81//! //     Int(i32),
82//! //     Float(f32),
83//! // }
84//! ```
85//!
86//! # Variant Naming
87//!
88//! The generator produces PascalCase variant names from C union field names:
89//!
90//! - Short names (≤2 chars): Derived from type (e.g., `i` with `int` → `Int`)
91//! - Long names: Converted to PascalCase (e.g., `int_value` → `IntValue`)
92//! - Type-based fallback: When field name is non-descriptive
93//!
94//! # Type Mapping
95//!
96//! C types are mapped to safe Rust equivalents:
97//!
98//! | C Type       | Rust Type  |
99//! |-------------|-----------|
100//! | `int`       | `i32`     |
101//! | `float`     | `f32`     |
102//! | `double`    | `f64`     |
103//! | `char`      | `u8`      |
104//! | `char*`     | `String`  |
105//! | `void`      | `()`      |
106//!
107//! # Quality Guarantees
108//!
109//! - ✅ Zero unsafe code in generated output
110//! - ✅ All variants derive Debug, Clone, PartialEq
111//! - ✅ Public visibility for API usage
112//! - ✅ Valid Rust syntax (parseable by rustc)
113//! - ✅ Exhaustive pattern matching enforced
114
115use decy_analyzer::tagged_union_analysis::TaggedUnionInfo;
116use decy_hir::HirType;
117
118/// Generator for Rust enums from C tagged unions.
119pub struct EnumGenerator;
120
121impl EnumGenerator {
122    /// Create a new enum generator.
123    pub fn new() -> Self {
124        Self
125    }
126
127    /// Generate a Rust enum from tagged union info.
128    ///
129    /// # Arguments
130    ///
131    /// * `info` - Tagged union information from analysis
132    ///
133    /// # Returns
134    ///
135    /// Rust enum definition as a string
136    pub fn generate_enum(&self, info: &TaggedUnionInfo) -> String {
137        let mut result = String::new();
138
139        // Add derive macros
140        result.push_str("#[derive(Debug, Clone, PartialEq)]\n");
141
142        // Add enum declaration
143        result.push_str(&format!("pub enum {} {{\n", info.struct_name));
144
145        // Generate variants
146        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            // Handle void types as unit variants
151            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    /// Capitalize and clean up variant name to PascalCase.
168    ///
169    /// For short names (<=2 chars), derives a better name from the type.
170    /// For longer names, converts to PascalCase.
171    fn capitalize_variant_name(name: &str, payload_type: &HirType) -> String {
172        // For very short names, derive from type
173        if name.len() <= 2 {
174            return Self::type_based_variant_name(payload_type);
175        }
176
177        // Split by underscore and capitalize each part
178        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            // Fallback: derive from type
192            Self::type_based_variant_name(payload_type)
193        } else {
194            parts.join("")
195        }
196    }
197
198    /// Generate a variant name based on the payload type.
199    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(), // DECY-158
205            HirType::Float => "Float".to_string(),
206            HirType::Double => "Double".to_string(),
207            HirType::Char => "Char".to_string(),
208            HirType::SignedChar => "SignedChar".to_string(), // DECY-250
209            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            // DECY-172: Type aliases use the alias name as variant name
225            HirType::TypeAlias(name) => name.clone(),
226        }
227    }
228
229    /// Map HIR type to Rust type string.
230    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(), // DECY-158
236            HirType::Float => "f32".to_string(),
237            HirType::Double => "f64".to_string(),
238            HirType::Char => "u8".to_string(),
239            HirType::SignedChar => "i8".to_string(), // DECY-250
240            HirType::Pointer(inner) => {
241                // Check if it's char* which should be String
242                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            // DECY-172: Preserve typedef names
291            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    // ============================================================================
308    // ENUM GENERATOR CONSTRUCTION TESTS
309    // ============================================================================
310
311    #[test]
312    fn test_enum_generator_new() {
313        let gen = EnumGenerator::new();
314        // Generator is a unit struct, just verify construction
315        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    // ============================================================================
325    // ENUM GENERATION TESTS
326    // ============================================================================
327
328    #[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        // Void variants should be unit variants (no payload)
397        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        // Last non-void variant should not have trailing comma
416        assert!(result.contains("Value(i32)\n}"));
417    }
418
419    // ============================================================================
420    // VARIANT NAME CAPITALIZATION TESTS
421    // ============================================================================
422
423    #[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        // Short names (<=2 chars) should derive from type
432        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"); // 2 chars is still "short"
446    }
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"); // 3 chars is not short
452    }
453
454    #[test]
455    fn test_capitalize_variant_name_empty_parts_fallback() {
456        // Name with only underscores should fall back to type-based name
457        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    // ============================================================================
474    // TYPE-BASED VARIANT NAME TESTS
475    // ============================================================================
476
477    #[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    // ============================================================================
609    // TYPE MAPPING TESTS
610    // ============================================================================
611
612    #[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    // ============================================================================
771    // INTEGRATION TESTS - COMPLETE ENUM GENERATION
772    // ============================================================================
773
774    #[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(), // Short name (1 char) → uses type
784                    payload_type: HirType::Int,
785                },
786                VariantInfo {
787                    name: "f".to_string(), // Short name (1 char) → uses type
788                    payload_type: HirType::Float,
789                },
790                VariantInfo {
791                    name: "s".to_string(), // Short name (1 char) → uses type
792                    payload_type: HirType::Pointer(Box::new(HirType::Char)),
793                },
794            ],
795        };
796
797        let result = gen.generate_enum(&info);
798
799        // Check derives
800        assert!(result.contains("#[derive(Debug, Clone, PartialEq)]"));
801
802        // Check enum declaration
803        assert!(result.contains("pub enum Value {"));
804
805        // Short names (<=2 chars) should use type-based variants
806        assert!(result.contains("Int(i32)"));
807        assert!(result.contains("Float(f32)"));
808        assert!(result.contains("String(String)")); // char* → String (type-based)
809    }
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}