Skip to main content

vox_codegen/targets/swift/
encode.rs

1//! Swift encoding expression/statement generation.
2//!
3//! Generates Swift code that encodes values into an `inout ByteBuffer`.
4//! All encode functions are void — they append into the buffer rather than
5//! returning a new `[UInt8]`.
6
7use super::types::swift_field_name;
8use facet_core::{ScalarType, Shape};
9use vox_types::{
10    EnumInfo, ShapeKind, StructInfo, VariantKind, classify_shape, classify_variant, is_bytes,
11};
12
13/// Generate a Swift encode statement for a given shape and value.
14/// The statement appends into the implicit `buffer: inout ByteBuffer` in scope.
15pub fn generate_encode_stmt(shape: &'static Shape, value: &str) -> String {
16    if is_bytes(shape) {
17        return format!("encodeByteSeq({value}, into: &buffer)");
18    }
19
20    match classify_shape(shape) {
21        ShapeKind::Scalar(scalar) => {
22            let fn_name = swift_encode_fn(scalar);
23            format!("{fn_name}({value}, into: &buffer)")
24        }
25        ShapeKind::List { element }
26        | ShapeKind::Slice { element }
27        | ShapeKind::Array { element, .. } => {
28            let inner = generate_encode_closure(element);
29            format!("encodeVec({value}, into: &buffer, encoder: {inner})")
30        }
31        ShapeKind::Option { inner } => {
32            let inner = generate_encode_closure(inner);
33            format!("encodeOption({value}, into: &buffer, encoder: {inner})")
34        }
35        ShapeKind::Tx { .. } | ShapeKind::Rx { .. } => {
36            format!("encodeVarint({value}.channelId, into: &buffer)")
37        }
38        ShapeKind::Tuple { elements } if elements.len() == 2 => {
39            let a = generate_encode_stmt(elements[0].shape, &format!("{value}.0"));
40            let b = generate_encode_stmt(elements[1].shape, &format!("{value}.1"));
41            format!("{a}\n{b}")
42        }
43        ShapeKind::TupleStruct { fields } if fields.len() == 2 => {
44            let a = generate_encode_stmt(fields[0].shape(), &format!("{value}.0"));
45            let b = generate_encode_stmt(fields[1].shape(), &format!("{value}.1"));
46            format!("{a}\n{b}")
47        }
48        ShapeKind::Struct(StructInfo {
49            name: Some(name), ..
50        }) => {
51            let fn_name = named_type_encode_fn_name(name);
52            format!("{fn_name}({value}, into: &buffer)")
53        }
54        ShapeKind::Struct(StructInfo {
55            name: None, fields, ..
56        }) => {
57            // Anonymous struct — encode each field inline
58            let stmts: Vec<String> = fields
59                .iter()
60                .map(|f| {
61                    let field_name = swift_field_name(f.name);
62                    generate_encode_stmt(f.shape(), &format!("{value}.{field_name}"))
63                })
64                .collect();
65            stmts.join("\n")
66        }
67        ShapeKind::Enum(EnumInfo {
68            name: Some(name), ..
69        }) => {
70            let fn_name = named_type_encode_fn_name(name);
71            format!("{fn_name}({value}, into: &buffer)")
72        }
73        ShapeKind::Enum(EnumInfo { name: None, .. }) => {
74            // Anonymous enum — inline switch
75            let closure = generate_encode_closure(shape);
76            format!("{closure}({value}, &buffer)")
77        }
78        ShapeKind::Pointer { pointee } => generate_encode_stmt(pointee, value),
79        ShapeKind::Result { ok, err } => {
80            let ok_stmt = generate_encode_stmt(ok, "v");
81            let err_stmt = generate_encode_stmt(err, "e");
82            format!(
83                "switch {value} {{\ncase .success(let v):\n    encodeVarint(UInt64(0), into: &buffer)\n    {ok_stmt}\ncase .failure(let e):\n    encodeVarint(UInt64(1), into: &buffer)\n    {err_stmt}\n}}"
84            )
85        }
86        _ => format!("/* unsupported encode for {value} */"),
87    }
88}
89
90/// Generate a Swift encode closure `(T, inout ByteBuffer) -> Void` for use with
91/// `encodeVec`, `encodeOption`, etc.
92pub fn generate_encode_closure(shape: &'static Shape) -> String {
93    if is_bytes(shape) {
94        return "{ val, buf in encodeByteSeq(val, into: &buf) }".into();
95    }
96
97    match classify_shape(shape) {
98        ShapeKind::Scalar(scalar) => {
99            let fn_name = swift_encode_fn(scalar);
100            format!("{{ val, buf in {fn_name}(val, into: &buf) }}")
101        }
102        ShapeKind::List { element } | ShapeKind::Slice { element } => {
103            let inner = generate_encode_closure(element);
104            format!("{{ val, buf in encodeVec(val, into: &buf, encoder: {inner}) }}")
105        }
106        ShapeKind::Option { inner } => {
107            let inner = generate_encode_closure(inner);
108            format!("{{ val, buf in encodeOption(val, into: &buf, encoder: {inner}) }}")
109        }
110        ShapeKind::Tx { .. } | ShapeKind::Rx { .. } => {
111            "{ val, buf in encodeVarint(val.channelId, into: &buf) }".into()
112        }
113        ShapeKind::Tuple { elements } if elements.len() == 2 => {
114            let a = encode_call_expr(elements[0].shape, "val.0", "buf");
115            let b = encode_call_expr(elements[1].shape, "val.1", "buf");
116            format!("{{ val, buf in {a}; {b} }}")
117        }
118        ShapeKind::TupleStruct { fields } if fields.len() == 2 => {
119            let a = encode_call_expr(fields[0].shape(), "val.0", "buf");
120            let b = encode_call_expr(fields[1].shape(), "val.1", "buf");
121            format!("{{ val, buf in {a}; {b} }}")
122        }
123        ShapeKind::Struct(StructInfo {
124            name: Some(name), ..
125        }) => {
126            let fn_name = named_type_encode_fn_name(name);
127            format!("{{ val, buf in {fn_name}(val, into: &buf) }}")
128        }
129        ShapeKind::Struct(StructInfo {
130            name: None, fields, ..
131        }) => {
132            // Anonymous struct — inline all field encodes
133            let stmts: Vec<String> = fields
134                .iter()
135                .map(|f| {
136                    let field_name = swift_field_name(f.name);
137                    let inner = generate_encode_closure(f.shape());
138                    format!("{inner}(val.{field_name}, &buf)")
139                })
140                .collect();
141            if stmts.is_empty() {
142                "{ _, _ in }".into()
143            } else {
144                format!("{{ val, buf in {} }}", stmts.join("; "))
145            }
146        }
147        ShapeKind::Enum(EnumInfo {
148            name: Some(name), ..
149        }) => {
150            let fn_name = named_type_encode_fn_name(name);
151            format!("{{ val, buf in {fn_name}(val, into: &buf) }}")
152        }
153        ShapeKind::Enum(EnumInfo {
154            name: None,
155            variants,
156        }) => {
157            // Anonymous enum — inline switch
158            let mut code = "{ val, buf in\nswitch val {\n".to_string();
159            for (i, v) in variants.iter().enumerate() {
160                let variant_name = swift_field_name(v.name);
161                match classify_variant(v) {
162                    VariantKind::Unit => {
163                        code.push_str(&format!(
164                            "case .{variant_name}: encodeVarint(UInt64({i}), into: &buf)\n"
165                        ));
166                    }
167                    VariantKind::Newtype { inner } => {
168                        let inner_closure = generate_encode_closure(inner);
169                        code.push_str(&format!(
170                            "case .{variant_name}(let v): encodeVarint(UInt64({i}), into: &buf); {inner_closure}(v, &buf)\n"
171                        ));
172                    }
173                    VariantKind::Tuple { fields } => {
174                        let bindings: Vec<String> =
175                            (0..fields.len()).map(|j| format!("f{j}")).collect();
176                        let stmts: Vec<String> = fields
177                            .iter()
178                            .enumerate()
179                            .map(|(j, f)| {
180                                let c = generate_encode_closure(f.shape());
181                                format!("{c}(f{j}, &buf)")
182                            })
183                            .collect();
184                        code.push_str(&format!(
185                            "case .{variant_name}({}): encodeVarint(UInt64({i}), into: &buf); {}\n",
186                            bindings
187                                .iter()
188                                .map(|b| format!("let {b}"))
189                                .collect::<Vec<_>>()
190                                .join(", "),
191                            stmts.join("; ")
192                        ));
193                    }
194                    VariantKind::Struct { fields } => {
195                        let bindings: Vec<String> =
196                            fields.iter().map(|f| swift_field_name(f.name)).collect();
197                        let stmts: Vec<String> = fields
198                            .iter()
199                            .map(|f| {
200                                let field_name = swift_field_name(f.name);
201                                let c = generate_encode_closure(f.shape());
202                                format!("{c}({field_name}, &buf)")
203                            })
204                            .collect();
205                        code.push_str(&format!(
206                            "case .{variant_name}({}): encodeVarint(UInt64({i}), into: &buf); {}\n",
207                            bindings
208                                .iter()
209                                .map(|b| format!("let {b}"))
210                                .collect::<Vec<_>>()
211                                .join(", "),
212                            stmts.join("; ")
213                        ));
214                    }
215                }
216            }
217            code.push_str("} }");
218            code
219        }
220        ShapeKind::Pointer { pointee } => generate_encode_closure(pointee),
221        ShapeKind::Result { ok, err } => {
222            let ok_closure = generate_encode_closure(ok);
223            let err_closure = generate_encode_closure(err);
224            format!(
225                "{{ val, buf in switch val {{ case .success(let v): encodeVarint(UInt64(0), into: &buf); {ok_closure}(v, &buf); case .failure(let e): encodeVarint(UInt64(1), into: &buf); {err_closure}(e, &buf) }} }}"
226            )
227        }
228        _ => "{ _, _ in /* unsupported */ }".into(),
229    }
230}
231
232/// Generate a top-level Swift encode function for a named struct or enum.
233/// e.g. `internal func encodeGnarlyPayload(_ value: GnarlyPayload, into buffer: inout ByteBuffer)`
234pub fn generate_named_type_encode_fn(name: &str, shape: &'static Shape) -> String {
235    let fn_name = named_type_encode_fn_name(name);
236    let mut out = String::new();
237    out.push_str(&format!(
238        "nonisolated internal func {fn_name}(_ value: {name}, into buffer: inout ByteBuffer) {{\n"
239    ));
240
241    match classify_shape(shape) {
242        ShapeKind::Struct(StructInfo { fields, .. }) => {
243            for f in fields {
244                let field_name = swift_field_name(f.name);
245                let stmt = generate_encode_stmt(f.shape(), &format!("value.{field_name}"));
246                for line in stmt.lines() {
247                    out.push_str(&format!("    {line}\n"));
248                }
249            }
250        }
251        ShapeKind::Enum(EnumInfo { variants, .. }) => {
252            out.push_str("    switch value {\n");
253            for (i, v) in variants.iter().enumerate() {
254                let variant_name = swift_field_name(v.name);
255                match classify_variant(v) {
256                    VariantKind::Unit => {
257                        out.push_str(&format!(
258                            "    case .{variant_name}:\n        encodeVarint(UInt64({i}), into: &buffer)\n"
259                        ));
260                    }
261                    VariantKind::Newtype { inner } => {
262                        let stmt = generate_encode_stmt(inner, "val");
263                        out.push_str(&format!(
264                            "    case .{variant_name}(let val):\n        encodeVarint(UInt64({i}), into: &buffer)\n"
265                        ));
266                        for line in stmt.lines() {
267                            out.push_str(&format!("        {line}\n"));
268                        }
269                    }
270                    VariantKind::Tuple { fields } => {
271                        let bindings: Vec<String> =
272                            (0..fields.len()).map(|j| format!("f{j}")).collect();
273                        let binding_str = bindings
274                            .iter()
275                            .map(|b| format!("let {b}"))
276                            .collect::<Vec<_>>()
277                            .join(", ");
278                        out.push_str(&format!(
279                            "    case .{variant_name}({binding_str}):\n        encodeVarint(UInt64({i}), into: &buffer)\n"
280                        ));
281                        for (j, f) in fields.iter().enumerate() {
282                            let stmt = generate_encode_stmt(f.shape(), &format!("f{j}"));
283                            for line in stmt.lines() {
284                                out.push_str(&format!("        {line}\n"));
285                            }
286                        }
287                    }
288                    VariantKind::Struct { fields } => {
289                        let bindings: Vec<String> =
290                            fields.iter().map(|f| swift_field_name(f.name)).collect();
291                        let binding_str = bindings
292                            .iter()
293                            .map(|b| format!("let {b}"))
294                            .collect::<Vec<_>>()
295                            .join(", ");
296                        out.push_str(&format!(
297                            "    case .{variant_name}({binding_str}):\n        encodeVarint(UInt64({i}), into: &buffer)\n"
298                        ));
299                        for f in fields {
300                            let field_name = swift_field_name(f.name);
301                            let stmt = generate_encode_stmt(f.shape(), &field_name);
302                            for line in stmt.lines() {
303                                out.push_str(&format!("        {line}\n"));
304                            }
305                        }
306                    }
307                }
308            }
309            out.push_str("    }\n");
310        }
311        _ => {}
312    }
313
314    out.push_str("}\n");
315    out
316}
317
318/// Generate encode functions for all named types.
319pub fn generate_named_type_encode_fns(named_types: &[(String, &'static Shape)]) -> String {
320    named_types
321        .iter()
322        .map(|(name, shape)| generate_named_type_encode_fn(name, shape))
323        .collect::<Vec<_>>()
324        .join("\n")
325}
326
327/// The name of the generated encode function for a named type.
328pub fn named_type_encode_fn_name(name: &str) -> String {
329    format!("encode{name}")
330}
331
332/// Generate a direct encode call expression (not a closure) for a value into a named buffer.
333/// Used inside closure bodies where closure IIFEs would be invalid Swift.
334fn encode_call_expr(shape: &'static Shape, value: &str, buf: &str) -> String {
335    if is_bytes(shape) {
336        return format!("encodeByteSeq({value}, into: &{buf})");
337    }
338    match classify_shape(shape) {
339        ShapeKind::Scalar(scalar) => {
340            let fn_name = swift_encode_fn(scalar);
341            format!("{fn_name}({value}, into: &{buf})")
342        }
343        ShapeKind::Struct(StructInfo {
344            name: Some(name), ..
345        })
346        | ShapeKind::Enum(EnumInfo {
347            name: Some(name), ..
348        }) => {
349            let fn_name = named_type_encode_fn_name(name);
350            format!("{fn_name}({value}, into: &{buf})")
351        }
352        ShapeKind::List { element } | ShapeKind::Slice { element } => {
353            let inner = generate_encode_closure(element);
354            format!("encodeVec({value}, into: &{buf}, encoder: {inner})")
355        }
356        ShapeKind::Option { inner } => {
357            let inner = generate_encode_closure(inner);
358            format!("encodeOption({value}, into: &{buf}, encoder: {inner})")
359        }
360        ShapeKind::Pointer { pointee } => encode_call_expr(pointee, value, buf),
361        ShapeKind::Tx { .. } | ShapeKind::Rx { .. } => {
362            format!("encodeVarint({value}.channelId, into: &{buf})")
363        }
364        _ => {
365            // Fallback: wrap in a named function call via closure
366            let closure = generate_encode_closure(shape);
367            format!("({closure})({value}, &{buf})")
368        }
369    }
370}
371
372/// Get the Swift encode function name for a scalar type.
373pub fn swift_encode_fn(scalar: ScalarType) -> &'static str {
374    match scalar {
375        ScalarType::Bool => "encodeBool",
376        ScalarType::U8 => "encodeU8",
377        ScalarType::I8 => "encodeI8",
378        ScalarType::U16 => "encodeU16",
379        ScalarType::I16 => "encodeI16",
380        ScalarType::U32 => "encodeU32",
381        ScalarType::I32 => "encodeI32",
382        ScalarType::U64 | ScalarType::USize => "encodeVarint",
383        ScalarType::I64 | ScalarType::ISize => "encodeI64",
384        ScalarType::F32 => "encodeF32",
385        ScalarType::F64 => "encodeF64",
386        ScalarType::Char | ScalarType::Str | ScalarType::CowStr | ScalarType::String => {
387            "encodeString"
388        }
389        ScalarType::Unit => "{ _, _ in }",
390        _ => "encodeByteSeq",
391    }
392}