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 facet_core::{ScalarType, Shape};
8use heck::ToLowerCamelCase;
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 = f.name.to_lower_camel_case();
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 = f.name.to_lower_camel_case();
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 = v.name.to_lower_camel_case();
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> = fields
196                            .iter()
197                            .map(|f| f.name.to_lower_camel_case())
198                            .collect();
199                        let stmts: Vec<String> = fields
200                            .iter()
201                            .map(|f| {
202                                let field_name = f.name.to_lower_camel_case();
203                                let c = generate_encode_closure(f.shape());
204                                format!("{c}({field_name}, &buf)")
205                            })
206                            .collect();
207                        code.push_str(&format!(
208                            "case .{variant_name}({}): encodeVarint(UInt64({i}), into: &buf); {}\n",
209                            bindings
210                                .iter()
211                                .map(|b| format!("let {b}"))
212                                .collect::<Vec<_>>()
213                                .join(", "),
214                            stmts.join("; ")
215                        ));
216                    }
217                }
218            }
219            code.push_str("} }");
220            code
221        }
222        ShapeKind::Pointer { pointee } => generate_encode_closure(pointee),
223        ShapeKind::Result { ok, err } => {
224            let ok_closure = generate_encode_closure(ok);
225            let err_closure = generate_encode_closure(err);
226            format!(
227                "{{ 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) }} }}"
228            )
229        }
230        _ => "{ _, _ in /* unsupported */ }".into(),
231    }
232}
233
234/// Generate a top-level Swift encode function for a named struct or enum.
235/// e.g. `internal func encodeGnarlyPayload(_ value: GnarlyPayload, into buffer: inout ByteBuffer)`
236pub fn generate_named_type_encode_fn(name: &str, shape: &'static Shape) -> String {
237    let fn_name = named_type_encode_fn_name(name);
238    let mut out = String::new();
239    out.push_str(&format!(
240        "nonisolated internal func {fn_name}(_ value: {name}, into buffer: inout ByteBuffer) {{\n"
241    ));
242
243    match classify_shape(shape) {
244        ShapeKind::Struct(StructInfo { fields, .. }) => {
245            for f in fields {
246                let field_name = f.name.to_lower_camel_case();
247                let stmt = generate_encode_stmt(f.shape(), &format!("value.{field_name}"));
248                for line in stmt.lines() {
249                    out.push_str(&format!("    {line}\n"));
250                }
251            }
252        }
253        ShapeKind::Enum(EnumInfo { variants, .. }) => {
254            out.push_str("    switch value {\n");
255            for (i, v) in variants.iter().enumerate() {
256                let variant_name = v.name.to_lower_camel_case();
257                match classify_variant(v) {
258                    VariantKind::Unit => {
259                        out.push_str(&format!(
260                            "    case .{variant_name}:\n        encodeVarint(UInt64({i}), into: &buffer)\n"
261                        ));
262                    }
263                    VariantKind::Newtype { inner } => {
264                        let stmt = generate_encode_stmt(inner, "val");
265                        out.push_str(&format!(
266                            "    case .{variant_name}(let val):\n        encodeVarint(UInt64({i}), into: &buffer)\n"
267                        ));
268                        for line in stmt.lines() {
269                            out.push_str(&format!("        {line}\n"));
270                        }
271                    }
272                    VariantKind::Tuple { fields } => {
273                        let bindings: Vec<String> =
274                            (0..fields.len()).map(|j| format!("f{j}")).collect();
275                        let binding_str = bindings
276                            .iter()
277                            .map(|b| format!("let {b}"))
278                            .collect::<Vec<_>>()
279                            .join(", ");
280                        out.push_str(&format!(
281                            "    case .{variant_name}({binding_str}):\n        encodeVarint(UInt64({i}), into: &buffer)\n"
282                        ));
283                        for (j, f) in fields.iter().enumerate() {
284                            let stmt = generate_encode_stmt(f.shape(), &format!("f{j}"));
285                            for line in stmt.lines() {
286                                out.push_str(&format!("        {line}\n"));
287                            }
288                        }
289                    }
290                    VariantKind::Struct { fields } => {
291                        let bindings: Vec<String> = fields
292                            .iter()
293                            .map(|f| f.name.to_lower_camel_case())
294                            .collect();
295                        let binding_str = bindings
296                            .iter()
297                            .map(|b| format!("let {b}"))
298                            .collect::<Vec<_>>()
299                            .join(", ");
300                        out.push_str(&format!(
301                            "    case .{variant_name}({binding_str}):\n        encodeVarint(UInt64({i}), into: &buffer)\n"
302                        ));
303                        for f in fields {
304                            let field_name = f.name.to_lower_camel_case();
305                            let stmt = generate_encode_stmt(f.shape(), &field_name);
306                            for line in stmt.lines() {
307                                out.push_str(&format!("        {line}\n"));
308                            }
309                        }
310                    }
311                }
312            }
313            out.push_str("    }\n");
314        }
315        _ => {}
316    }
317
318    out.push_str("}\n");
319    out
320}
321
322/// Generate encode functions for all named types.
323pub fn generate_named_type_encode_fns(named_types: &[(String, &'static Shape)]) -> String {
324    named_types
325        .iter()
326        .map(|(name, shape)| generate_named_type_encode_fn(name, shape))
327        .collect::<Vec<_>>()
328        .join("\n")
329}
330
331/// The name of the generated encode function for a named type.
332pub fn named_type_encode_fn_name(name: &str) -> String {
333    format!("encode{name}")
334}
335
336/// Generate a direct encode call expression (not a closure) for a value into a named buffer.
337/// Used inside closure bodies where closure IIFEs would be invalid Swift.
338fn encode_call_expr(shape: &'static Shape, value: &str, buf: &str) -> String {
339    if is_bytes(shape) {
340        return format!("encodeByteSeq({value}, into: &{buf})");
341    }
342    match classify_shape(shape) {
343        ShapeKind::Scalar(scalar) => {
344            let fn_name = swift_encode_fn(scalar);
345            format!("{fn_name}({value}, into: &{buf})")
346        }
347        ShapeKind::Struct(StructInfo {
348            name: Some(name), ..
349        })
350        | ShapeKind::Enum(EnumInfo {
351            name: Some(name), ..
352        }) => {
353            let fn_name = named_type_encode_fn_name(name);
354            format!("{fn_name}({value}, into: &{buf})")
355        }
356        ShapeKind::List { element } | ShapeKind::Slice { element } => {
357            let inner = generate_encode_closure(element);
358            format!("encodeVec({value}, into: &{buf}, encoder: {inner})")
359        }
360        ShapeKind::Option { inner } => {
361            let inner = generate_encode_closure(inner);
362            format!("encodeOption({value}, into: &{buf}, encoder: {inner})")
363        }
364        ShapeKind::Pointer { pointee } => encode_call_expr(pointee, value, buf),
365        ShapeKind::Tx { .. } | ShapeKind::Rx { .. } => {
366            format!("encodeVarint({value}.channelId, into: &{buf})")
367        }
368        _ => {
369            // Fallback: wrap in a named function call via closure
370            let closure = generate_encode_closure(shape);
371            format!("({closure})({value}, &{buf})")
372        }
373    }
374}
375
376/// Get the Swift encode function name for a scalar type.
377pub fn swift_encode_fn(scalar: ScalarType) -> &'static str {
378    match scalar {
379        ScalarType::Bool => "encodeBool",
380        ScalarType::U8 => "encodeU8",
381        ScalarType::I8 => "encodeI8",
382        ScalarType::U16 => "encodeU16",
383        ScalarType::I16 => "encodeI16",
384        ScalarType::U32 => "encodeU32",
385        ScalarType::I32 => "encodeI32",
386        ScalarType::U64 | ScalarType::USize => "encodeVarint",
387        ScalarType::I64 | ScalarType::ISize => "encodeI64",
388        ScalarType::F32 => "encodeF32",
389        ScalarType::F64 => "encodeF64",
390        ScalarType::Char | ScalarType::Str | ScalarType::CowStr | ScalarType::String => {
391            "encodeString"
392        }
393        ScalarType::Unit => "{ _, _ in }",
394        _ => "encodeByteSeq",
395    }
396}