witx_codegen/rust/
union.rs

1use std::io::Write;
2
3use super::*;
4
5impl RustGenerator {
6    fn define_union_member_accessors<T: Write>(
7        w: &mut PrettyWriter<T>,
8        _union_name: &str,
9        i: usize,
10        member: &ASUnionMember,
11        inner_name: &str,
12    ) -> Result<(), Error> {
13        let name = &member.name;
14        let member_is_void = matches!(member.type_.as_ref(), ASType::Void);
15
16        if member_is_void {
17            // new_*
18            w.write_line(format!("pub fn new_{}() -> Self {{", name.as_fn_suffix(),))?;
19            {
20                let mut w = w.new_block();
21                w.write_line(format!("Self::new({})", i))?;
22            }
23            w.write_line("}")?.eob()?;
24        } else {
25            // !member_is_void
26            // new_*
27            w.write_line(format!(
28                "pub fn new_{}(val: {}) -> Self {{",
29                name.as_fn_suffix(),
30                member.type_.as_lang()
31            ))?;
32            {
33                let mut w = w.new_block();
34                w.write_line(format!("let mut tu = Self::new({});", i))?;
35                w.write_line(format!(
36                    "tu.member = std::mem::MaybeUninit::new({} {{ {}: val }});",
37                    inner_name.as_type(),
38                    member.name.as_var()
39                ))?;
40                w.write_line("tu")?;
41            }
42            w.write_line("}")?.eob()?;
43
44            // get_*
45            w.write_line(format!(
46                "pub fn into_{}(self) -> {} {{",
47                name.as_fn_suffix(),
48                member.type_.as_lang()
49            ))?;
50            {
51                let mut w = w.new_block();
52                w.write_line(format!("assert_eq!(self.tag, {});", i))?;
53                w.write_line(format!(
54                    "unsafe {{ self.member.assume_init().{} }}",
55                    member.name.as_var()
56                ))?;
57            }
58            w.write_line("}")?.eob()?;
59
60            // set_*
61            w.write_line(format!(
62                "pub fn set_{}(&mut self, val: {}) {{",
63                name.as_fn_suffix(),
64                member.type_.as_lang()
65            ))?;
66            {
67                let mut w = w.new_block();
68                w.write_line(format!("assert_eq!(self.tag, {});", i))?;
69                w.write_line(format!(
70                    "let uval = {} {{ {}: val }};",
71                    inner_name.as_type(),
72                    member.name.as_var()
73                ))?;
74                w.write_line("unsafe { *self.member.as_mut_ptr() = uval };")?;
75            }
76            w.write_line("}")?.eob()?;
77        }
78
79        // is_*
80        w.write_line(format!(
81            "pub fn is_{}(&self) -> bool {{",
82            name.as_fn_suffix()
83        ))?;
84        {
85            let mut w = w.new_block();
86            w.write_line(format!("self.tag == {}", i))?;
87        }
88        w.write_line("}")?.eob()?;
89
90        Ok(())
91    }
92
93    fn define_union_member<T: Write>(
94        w: &mut PrettyWriter<T>,
95        union_name: &str,
96        i: usize,
97        member: &ASUnionMember,
98        inner_name: &str,
99    ) -> Result<(), Error> {
100        let member_type = member.type_.as_ref();
101        match member_type {
102            ASType::Void => {
103                w.write_line(format!(
104                    "// --- {}: (no associated content) if tag={}",
105                    member.name.as_var(),
106                    i
107                ))?;
108            }
109            _ => {
110                w.write_line(format!(
111                    "// --- {}: {} if tag={}",
112                    member.name.as_var(),
113                    member_type.as_lang(),
114                    i
115                ))?;
116            }
117        }
118        w.eob()?;
119        Self::define_union_member_accessors(w, union_name, i, member, inner_name)?;
120        Ok(())
121    }
122
123    pub fn define_as_union<T: Write>(
124        w: &mut PrettyWriter<T>,
125        name: &str,
126        union_: &ASUnion,
127    ) -> Result<(), Error> {
128        let tag_repr = union_.tag_repr.as_ref();
129        let inner_name = format!("{}_member", name);
130        w.write_line("#[repr(C)]")?
131            .write_line(format!("pub union {} {{", inner_name.as_type()))?;
132        {
133            let mut w = w.new_block();
134            for (i, member) in union_.members.iter().enumerate() {
135                let member_is_void = matches!(member.type_.as_ref(), ASType::Void);
136                if member_is_void {
137                    w.write_line(format!(
138                        "// {} with no associated value if tag={}",
139                        member.name.as_var(),
140                        i
141                    ))?;
142                } else {
143                    w.write_line(format!(
144                        "{}: {}, // if tag={}",
145                        member.name.as_var(),
146                        member.type_.as_lang(),
147                        i
148                    ))?;
149                }
150            }
151        }
152        w.write_line("}")?;
153        w.eob()?;
154
155        w.write_line("#[repr(C, packed)]")?
156            .write_line(format!("pub struct {} {{", name.as_type()))?;
157        {
158            let mut w = w.new_block();
159            w.write_line(format!("pub tag: {},", tag_repr.as_lang()))?;
160            let pad_len = union_.padding_after_tag;
161            for i in 0..(pad_len & 1) {
162                w.write_line(format!("__pad8_{}: u8,", i))?;
163            }
164            for i in 0..(pad_len & 3) / 2 {
165                w.write_line(format!("__pad16_{}: u16,", i))?;
166            }
167            for i in 0..(pad_len & 7) / 4 {
168                w.write_line(format!("__pad32_{}: u32,", i))?;
169            }
170            for i in 0..pad_len / 8 {
171                w.write_line(format!("__pad64_{}: u64,", i))?;
172            }
173            w.write_line(format!(
174                "pub member: std::mem::MaybeUninit<{}>,",
175                inner_name.as_type()
176            ))?;
177        }
178        w.write_line("}")?;
179        w.eob()?;
180
181        w.write_line(format!("impl {} {{", name.as_type()))?;
182        {
183            let mut w = w.new_block();
184            w.write_line(format!("fn new(tag: {}) -> Self {{", tag_repr.as_lang()))?;
185            {
186                let mut w = w.new_block();
187                w.write_line("let mut tu = unsafe { std::mem::zeroed::<Self>() };")?;
188                w.write_line("tu.tag = tag;")?;
189                w.write_line("tu")?;
190            }
191            w.write_line("}")?.eob()?;
192
193            for (i, member) in union_.members.iter().enumerate() {
194                w.eob()?;
195                Self::define_union_member(&mut w, name, i, member, &inner_name)?;
196            }
197        }
198        w.write_line("}")?.eob()?;
199        Ok(())
200    }
201}