Skip to main content

vexil_codegen_rust/
flags.rs

1use vexil_lang::ir::{FlagsDef, TypeRegistry};
2
3use crate::annotations::{emit_tombstones, emit_type_annotations};
4use crate::emit::CodeWriter;
5
6/// Convert a `SmolStr` name to UPPER_SNAKE_CASE.
7fn to_upper_snake(name: &str) -> String {
8    let mut out = String::with_capacity(name.len() + 4);
9    let mut prev_lower = false;
10    for ch in name.chars() {
11        if ch.is_uppercase() && prev_lower {
12            out.push('_');
13        }
14        out.push(ch.to_ascii_uppercase());
15        prev_lower = ch.is_lowercase();
16    }
17    out
18}
19
20/// Emit a complete flags newtype struct with bit constants, utility methods,
21/// bitwise operator impls, and `Pack`/`Unpack` implementations.
22pub fn emit_flags(w: &mut CodeWriter, flags: &FlagsDef, _registry: &TypeRegistry) {
23    let name = flags.name.as_str();
24
25    // ── Tombstone block ─────────────────────────────────────────────────────
26    emit_tombstones(w, name, &flags.tombstones);
27
28    // ── Type-level annotations (doc, since, deprecated, non_exhaustive) ─────
29    emit_type_annotations(w, &flags.annotations);
30
31    // ── Derive ───────────────────────────────────────────────────────────────
32    w.line("#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]");
33
34    // ── Struct definition ────────────────────────────────────────────────────
35    w.line(&format!("pub struct {name}(pub u64);"));
36    w.blank();
37
38    // ── impl block ───────────────────────────────────────────────────────────
39    w.open_block(&format!("impl {name}"));
40
41    // Bit constants
42    for bit_def in &flags.bits {
43        let const_name = to_upper_snake(bit_def.name.as_str());
44        let bit = bit_def.bit;
45        // Field-level annotations
46        for doc in &bit_def.annotations.doc {
47            w.line(&format!("/// {doc}"));
48        }
49        if let Some(ref dep) = bit_def.annotations.deprecated {
50            match &dep.since {
51                Some(since) => w.line(&format!(
52                    "#[deprecated(since = \"{since}\", note = \"{}\")]",
53                    dep.reason
54                )),
55                None => w.line(&format!("#[deprecated(note = \"{}\")]", dep.reason)),
56            }
57        }
58        w.line(&format!(
59            "pub const {const_name}: Self = Self(1_u64 << {bit}_u32);"
60        ));
61    }
62
63    w.blank();
64
65    // Utility methods
66    w.line("pub const fn contains(self, other: Self) -> bool { self.0 & other.0 == other.0 }");
67    w.line("pub const fn is_empty(self) -> bool { self.0 == 0 }");
68    w.line("pub const fn empty() -> Self { Self(0) }");
69
70    w.close_block();
71    w.blank();
72
73    // ── BitOr ────────────────────────────────────────────────────────────────
74    w.open_block(&format!("impl std::ops::BitOr for {name}"));
75    w.line("type Output = Self;");
76    w.open_block("fn bitor(self, rhs: Self) -> Self");
77    w.line("Self(self.0 | rhs.0)");
78    w.close_block();
79    w.close_block();
80    w.blank();
81
82    // ── BitAnd ───────────────────────────────────────────────────────────────
83    w.open_block(&format!("impl std::ops::BitAnd for {name}"));
84    w.line("type Output = Self;");
85    w.open_block("fn bitand(self, rhs: Self) -> Self");
86    w.line("Self(self.0 & rhs.0)");
87    w.close_block();
88    w.close_block();
89    w.blank();
90
91    // ── Not ──────────────────────────────────────────────────────────────────
92    w.open_block(&format!("impl std::ops::Not for {name}"));
93    w.line("type Output = Self;");
94    w.open_block("fn not(self) -> Self");
95    w.line("Self(!self.0)");
96    w.close_block();
97    w.close_block();
98    w.blank();
99
100    // ── Pack impl ────────────────────────────────────────────────────────────
101    w.open_block(&format!("impl vexil_runtime::Pack for {name}"));
102    w.open_block(
103        "fn pack(&self, w: &mut vexil_runtime::BitWriter) -> Result<(), vexil_runtime::EncodeError>",
104    );
105    match flags.wire_bytes {
106        1 => w.line("w.write_u8(self.0 as u8);"),
107        2 => w.line("w.write_u16(self.0 as u16);"),
108        4 => w.line("w.write_u32(self.0 as u32);"),
109        _ => w.line("w.write_u64(self.0);"),
110    }
111    w.line("Ok(())");
112    w.close_block();
113    w.close_block();
114    w.blank();
115
116    // ── Unpack impl ──────────────────────────────────────────────────────────
117    w.open_block(&format!("impl vexil_runtime::Unpack for {name}"));
118    w.open_block(
119        "fn unpack(r: &mut vexil_runtime::BitReader<'_>) -> Result<Self, vexil_runtime::DecodeError>",
120    );
121    match flags.wire_bytes {
122        1 => w.line("let raw = r.read_u8()? as u64;"),
123        2 => w.line("let raw = r.read_u16()? as u64;"),
124        4 => w.line("let raw = r.read_u32()? as u64;"),
125        _ => w.line("let raw = r.read_u64()?;"),
126    }
127    w.line("Ok(Self(raw))");
128    w.close_block();
129    w.close_block();
130    w.blank();
131}