Skip to main content

vexil_codegen_ts/
enum_gen.rs

1use vexil_lang::ir::{EnumDef, TypeRegistry};
2
3use crate::emit::CodeWriter;
4
5/// Emit a complete enum: string literal union + const object + encode/decode.
6pub fn emit_enum(w: &mut CodeWriter, en: &EnumDef, _registry: &TypeRegistry) {
7    let name = en.name.as_str();
8    let wire_bits = en.wire_bits;
9    let non_exhaustive = en.annotations.non_exhaustive;
10
11    // Type: string literal union
12    if non_exhaustive {
13        // Non-exhaustive enums include an unknown string fallback
14        let literals: Vec<String> = en
15            .variants
16            .iter()
17            .map(|v| format!("'{}'", v.name))
18            .collect();
19        let mut all = literals;
20        all.push("string".to_string());
21        w.line(&format!("export type {name} = {};", all.join(" | ")));
22    } else {
23        let literals: Vec<String> = en
24            .variants
25            .iter()
26            .map(|v| format!("'{}'", v.name))
27            .collect();
28        w.line(&format!("export type {name} = {};", literals.join(" | ")));
29    }
30
31    // Const object for convenient access
32    w.open_block(&format!("export const {name} ="));
33    for variant in &en.variants {
34        w.line(&format!("{}: '{}' as const,", variant.name, variant.name));
35    }
36    w.dedent();
37    w.line("} as const;");
38    w.blank();
39
40    // Encode function
41    w.open_block(&format!(
42        "export function encode{name}(v: {name}, w: BitWriter): void"
43    ));
44    // Build ordinal map
45    w.line("let disc: number;");
46    w.open_block("switch (v)");
47    for variant in &en.variants {
48        w.line(&format!(
49            "case '{}': disc = {}; break;",
50            variant.name, variant.ordinal
51        ));
52    }
53    w.line(&format!(
54        "default: throw new Error(`Unknown {name} variant: ${{v}}`);",
55    ));
56    w.close_block();
57    w.line(&format!("w.writeBits(disc, {wire_bits});"));
58    w.close_block();
59    w.blank();
60
61    // Decode function
62    w.open_block(&format!(
63        "export function decode{name}(r: BitReader): {name}"
64    ));
65    w.line(&format!("const disc = r.readBits({wire_bits});"));
66    w.open_block("switch (disc)");
67    for variant in &en.variants {
68        w.line(&format!(
69            "case {}: return '{}';",
70            variant.ordinal, variant.name
71        ));
72    }
73    if non_exhaustive {
74        w.line("default: return `Unknown(${disc})`;");
75    } else {
76        w.line(&format!(
77            "default: throw new Error(`Unknown {name} discriminant: ${{disc}}`);",
78        ));
79    }
80    w.close_block();
81    w.close_block();
82    w.blank();
83}