Skip to main content

vox_codegen/targets/swift/
decode.rs

1//! Swift decoding statement generation.
2//!
3//! Generates Swift code that decodes values from an `inout ByteBuffer`.
4//! All decode functions take `inout ByteBuffer` and advance its reader index.
5
6use facet_core::{ScalarType, Shape};
7use heck::ToLowerCamelCase;
8use vox_types::{
9    EnumInfo, ShapeKind, StructInfo, VariantKind, classify_shape, classify_variant, is_bytes,
10};
11
12/// Generate a Swift decode statement for a given shape.
13/// Returns code that decodes from `buffer` into a variable named `var_name`.
14pub fn generate_decode_stmt(shape: &'static Shape, var_name: &str, indent: &str) -> String {
15    generate_decode_stmt_impl(shape, var_name, indent, "buffer")
16}
17
18/// Generate a Swift decode statement for a given shape using a custom cursor variable name.
19/// (cursor_var is now ignored — kept for call-site compatibility, buffer is always `buffer`)
20pub fn generate_decode_stmt_with_cursor(
21    shape: &'static Shape,
22    var_name: &str,
23    indent: &str,
24    _cursor_var: &str,
25) -> String {
26    generate_decode_stmt_impl(shape, var_name, indent, "buffer")
27}
28
29/// Generate a Swift decode statement from a specific data variable.
30/// (data_var is now ignored — kept for call-site compatibility, buffer is always `buffer`)
31pub fn generate_decode_stmt_from(
32    shape: &'static Shape,
33    var_name: &str,
34    indent: &str,
35    _data_var: &str,
36) -> String {
37    generate_decode_stmt_impl(shape, var_name, indent, "buffer")
38}
39
40/// Generate a Swift decode statement from a specific data variable and cursor.
41/// (data_var and cursor_var are now ignored — kept for call-site compatibility)
42pub fn generate_decode_stmt_from_with_cursor(
43    shape: &'static Shape,
44    var_name: &str,
45    indent: &str,
46    _data_var: &str,
47    _cursor_var: &str,
48) -> String {
49    generate_decode_stmt_impl(shape, var_name, indent, "buffer")
50}
51
52/// Core implementation: generate a decode statement that reads from the named buffer variable.
53pub fn generate_decode_stmt_with_buf(
54    shape: &'static Shape,
55    var_name: &str,
56    indent: &str,
57    buf_name: &str,
58) -> String {
59    generate_decode_stmt_impl(shape, var_name, indent, buf_name)
60}
61
62fn generate_decode_stmt_impl(
63    shape: &'static Shape,
64    var_name: &str,
65    indent: &str,
66    buf_name: &str,
67) -> String {
68    // bytes → ByteBuffer slice, presented as Data for the user-facing type
69    if is_bytes(shape) {
70        return format!(
71            "{indent}var _{var_name}_buf = try decodeBytes(from: &{buf_name})\n{indent}let {var_name} = Data(_{var_name}_buf.readBytes(length: _{var_name}_buf.readableBytes) ?? [])\n"
72        );
73    }
74
75    match classify_shape(shape) {
76        ShapeKind::Scalar(scalar) => {
77            let decode_fn = swift_decode_fn(scalar);
78            format!("{indent}let {var_name} = try {decode_fn}(from: &{buf_name})\n")
79        }
80        ShapeKind::List { element }
81        | ShapeKind::Slice { element }
82        | ShapeKind::Array { element, .. } => {
83            let inner = generate_decode_closure(element);
84            format!("{indent}let {var_name} = try decodeVec(from: &{buf_name}, decoder: {inner})\n")
85        }
86        ShapeKind::Option { inner } => {
87            let inner = generate_decode_closure(inner);
88            format!(
89                "{indent}let {var_name} = try decodeOption(from: &{buf_name}, decoder: {inner})\n"
90            )
91        }
92        ShapeKind::Tuple { elements } if elements.len() == 2 => {
93            let a = generate_decode_closure(elements[0].shape);
94            let b = generate_decode_closure(elements[1].shape);
95            format!(
96                "{indent}let {var_name} = try decodeTuple2(from: &{buf_name}, decoderA: {a}, decoderB: {b})\n"
97            )
98        }
99        ShapeKind::TupleStruct { fields } if fields.len() == 2 => {
100            let a = generate_decode_closure(fields[0].shape());
101            let b = generate_decode_closure(fields[1].shape());
102            format!(
103                "{indent}let {var_name} = try decodeTuple2(from: &{buf_name}, decoderA: {a}, decoderB: {b})\n"
104            )
105        }
106        ShapeKind::Struct(StructInfo {
107            name: Some(name),
108            fields,
109            ..
110        }) => {
111            // Named struct — decode each field then construct
112            let mut out = String::new();
113            for f in fields.iter() {
114                let field_name = f.name.to_lower_camel_case();
115                out.push_str(&generate_decode_stmt_impl(
116                    f.shape(),
117                    &format!("_{var_name}_{field_name}"),
118                    indent,
119                    buf_name,
120                ));
121            }
122            let field_inits: Vec<String> = fields
123                .iter()
124                .map(|f| {
125                    let field_name = f.name.to_lower_camel_case();
126                    format!("{field_name}: _{var_name}_{field_name}")
127                })
128                .collect();
129            out.push_str(&format!(
130                "{indent}let {var_name} = {name}({})\n",
131                field_inits.join(", ")
132            ));
133            out
134        }
135        ShapeKind::Enum(EnumInfo {
136            name: Some(name),
137            variants,
138            ..
139        }) => {
140            let mut out = String::new();
141            out.push_str(&format!(
142                "{indent}let _{var_name}_disc = try decodeVarint(from: &{buf_name})\n"
143            ));
144            out.push_str(&format!("{indent}let {var_name}: {name}\n"));
145            out.push_str(&format!("{indent}switch _{var_name}_disc {{\n"));
146            for (i, v) in variants.iter().enumerate() {
147                out.push_str(&format!("{indent}case {i}:\n"));
148                let inner_indent = format!("{indent}    ");
149                match classify_variant(v) {
150                    VariantKind::Unit => {
151                        out.push_str(&format!(
152                            "{inner_indent}{var_name} = .{}\n",
153                            v.name.to_lower_camel_case()
154                        ));
155                    }
156                    VariantKind::Newtype { inner } => {
157                        out.push_str(&generate_decode_stmt_impl(
158                            inner,
159                            &format!("_{var_name}_val"),
160                            &inner_indent,
161                            buf_name,
162                        ));
163                        out.push_str(&format!(
164                            "{inner_indent}{var_name} = .{}(_{var_name}_val)\n",
165                            v.name.to_lower_camel_case()
166                        ));
167                    }
168                    VariantKind::Tuple { fields } => {
169                        for (j, f) in fields.iter().enumerate() {
170                            out.push_str(&generate_decode_stmt_impl(
171                                f.shape(),
172                                &format!("_{var_name}_f{j}"),
173                                &inner_indent,
174                                buf_name,
175                            ));
176                        }
177                        let args: Vec<String> = (0..fields.len())
178                            .map(|j| format!("_{var_name}_f{j}"))
179                            .collect();
180                        out.push_str(&format!(
181                            "{inner_indent}{var_name} = .{}({})\n",
182                            v.name.to_lower_camel_case(),
183                            args.join(", ")
184                        ));
185                    }
186                    VariantKind::Struct { fields } => {
187                        for f in fields.iter() {
188                            let field_name = f.name.to_lower_camel_case();
189                            out.push_str(&generate_decode_stmt_impl(
190                                f.shape(),
191                                &format!("_{var_name}_{field_name}"),
192                                &inner_indent,
193                                buf_name,
194                            ));
195                        }
196                        let args: Vec<String> = fields
197                            .iter()
198                            .map(|f| {
199                                let field_name = f.name.to_lower_camel_case();
200                                format!("{field_name}: _{var_name}_{field_name}")
201                            })
202                            .collect();
203                        out.push_str(&format!(
204                            "{inner_indent}{var_name} = .{}({})\n",
205                            v.name.to_lower_camel_case(),
206                            args.join(", ")
207                        ));
208                    }
209                }
210            }
211            out.push_str(&format!("{indent}default:\n"));
212            out.push_str(&format!(
213                "{indent}    throw VoxError.decodeError(\"unknown enum variant\")\n"
214            ));
215            out.push_str(&format!("{indent}}}\n"));
216            out
217        }
218        ShapeKind::Pointer { pointee } => {
219            generate_decode_stmt_impl(pointee, var_name, indent, buf_name)
220        }
221        ShapeKind::Result { ok, err } => {
222            let ok_type = super::types::swift_type_base(ok);
223            let err_type = super::types::swift_type_base(err);
224            let mut out = String::new();
225            out.push_str(&format!(
226                "{indent}let _{var_name}_disc = try decodeVarint(from: &{buf_name})\n"
227            ));
228            out.push_str(&format!(
229                "{indent}let {var_name}: Result<{ok_type}, {err_type}>\n"
230            ));
231            out.push_str(&format!("{indent}switch _{var_name}_disc {{\n"));
232            out.push_str(&format!("{indent}case 0:\n"));
233            let inner_indent = format!("{indent}    ");
234            out.push_str(&generate_decode_stmt_impl(
235                ok,
236                &format!("_{var_name}_ok"),
237                &inner_indent,
238                buf_name,
239            ));
240            out.push_str(&format!(
241                "{inner_indent}{var_name} = .success(_{var_name}_ok)\n"
242            ));
243            out.push_str(&format!("{indent}case 1:\n"));
244            out.push_str(&generate_decode_stmt_impl(
245                err,
246                &format!("_{var_name}_err"),
247                &inner_indent,
248                buf_name,
249            ));
250            out.push_str(&format!(
251                "{inner_indent}{var_name} = .failure(_{var_name}_err)\n"
252            ));
253            out.push_str(&format!("{indent}default:\n"));
254            out.push_str(&format!(
255                "{indent}    throw VoxError.decodeError(\"invalid Result discriminant\")\n"
256            ));
257            out.push_str(&format!("{indent}}}\n"));
258            out
259        }
260        _ => {
261            format!("{indent}let {var_name}: Any = () // unsupported type\n")
262        }
263    }
264}
265
266/// Generate a Swift decode closure `(inout ByteBuffer) throws -> T` for use with
267/// `decodeVec`, `decodeOption`, etc.
268pub fn generate_decode_closure(shape: &'static Shape) -> String {
269    if is_bytes(shape) {
270        // decodeBytes returns ByteBuffer; convert to Data for user-facing type
271        return "{ buf in var _b = try decodeBytes(from: &buf); return Data(_b.readBytes(length: _b.readableBytes) ?? []) }".into();
272    }
273
274    match classify_shape(shape) {
275        ShapeKind::Scalar(scalar) => {
276            let decode_fn = swift_decode_fn(scalar);
277            format!("{{ buf in try {decode_fn}(from: &buf) }}")
278        }
279        ShapeKind::List { element } | ShapeKind::Slice { element } => {
280            let inner = generate_decode_closure(element);
281            format!("{{ buf in try decodeVec(from: &buf, decoder: {inner}) }}")
282        }
283        ShapeKind::Option { inner } => {
284            let inner = generate_decode_closure(inner);
285            format!("{{ buf in try decodeOption(from: &buf, decoder: {inner}) }}")
286        }
287        ShapeKind::Tuple { elements } if elements.len() == 2 => {
288            let a = generate_decode_closure(elements[0].shape);
289            let b = generate_decode_closure(elements[1].shape);
290            format!("{{ buf in try decodeTuple2(from: &buf, decoderA: {a}, decoderB: {b}) }}")
291        }
292        ShapeKind::TupleStruct { fields } if fields.len() == 2 => {
293            let a = generate_decode_closure(fields[0].shape());
294            let b = generate_decode_closure(fields[1].shape());
295            format!("{{ buf in try decodeTuple2(from: &buf, decoderA: {a}, decoderB: {b}) }}")
296        }
297        ShapeKind::Struct(StructInfo {
298            name: Some(name),
299            fields,
300            ..
301        }) => {
302            // Named struct — inline decode all fields then construct
303            let mut code = "{ buf in\n".to_string();
304            for f in fields.iter() {
305                let field_name = f.name.to_lower_camel_case();
306                let inner = generate_decode_closure(f.shape());
307                code.push_str(&format!("    let _{field_name} = try ({inner})(&buf)\n"));
308            }
309            let field_inits: Vec<String> = fields
310                .iter()
311                .map(|f| {
312                    let field_name = f.name.to_lower_camel_case();
313                    format!("{field_name}: _{field_name}")
314                })
315                .collect();
316            code.push_str(&format!(
317                "    return {name}({})\n}}",
318                field_inits.join(", ")
319            ));
320            code
321        }
322        ShapeKind::Enum(EnumInfo {
323            name: Some(name),
324            variants,
325            ..
326        }) => {
327            let mut code = format!(
328                "{{ buf in\n    let disc = try decodeVarint(from: &buf)\n    let result: {name}\n    switch disc {{\n"
329            );
330            for (i, v) in variants.iter().enumerate() {
331                code.push_str(&format!("    case {i}:\n"));
332                match classify_variant(v) {
333                    VariantKind::Unit => {
334                        code.push_str(&format!(
335                            "        result = .{}\n",
336                            v.name.to_lower_camel_case()
337                        ));
338                    }
339                    VariantKind::Newtype { inner } => {
340                        let inner_closure = generate_decode_closure(inner);
341                        code.push_str(&format!(
342                            "        let val = try ({inner_closure})(&buf)\n        result = .{}(val)\n",
343                            v.name.to_lower_camel_case()
344                        ));
345                    }
346                    VariantKind::Tuple { fields } => {
347                        for (j, f) in fields.iter().enumerate() {
348                            let inner = generate_decode_closure(f.shape());
349                            code.push_str(&format!("        let f{j} = try ({inner})(&buf)\n"));
350                        }
351                        let args: Vec<String> =
352                            (0..fields.len()).map(|j| format!("f{j}")).collect();
353                        code.push_str(&format!(
354                            "        result = .{}({})\n",
355                            v.name.to_lower_camel_case(),
356                            args.join(", ")
357                        ));
358                    }
359                    VariantKind::Struct { fields } => {
360                        for f in fields.iter() {
361                            let field_name = f.name.to_lower_camel_case();
362                            let inner = generate_decode_closure(f.shape());
363                            code.push_str(&format!(
364                                "        let _{field_name} = try ({inner})(&buf)\n"
365                            ));
366                        }
367                        let args: Vec<String> = fields
368                            .iter()
369                            .map(|f| {
370                                let field_name = f.name.to_lower_camel_case();
371                                format!("{field_name}: _{field_name}")
372                            })
373                            .collect();
374                        code.push_str(&format!(
375                            "        result = .{}({})\n",
376                            v.name.to_lower_camel_case(),
377                            args.join(", ")
378                        ));
379                    }
380                }
381            }
382            code.push_str(
383                "    default:\n        throw VoxError.decodeError(\"unknown enum variant\")\n    }\n    return result\n}",
384            );
385            code
386        }
387        ShapeKind::Pointer { pointee } => generate_decode_closure(pointee),
388        _ => "{ _ in throw VoxError.decodeError(\"unsupported type\") }".into(),
389    }
390}
391
392/// Generate inline decode expression — just the expression part, no `let x =`.
393/// Used internally where a closure calls another closure.
394pub fn generate_inline_decode(shape: &'static Shape, _data_var: &str, _offset_var: &str) -> String {
395    // data_var and offset_var are ignored — we always use `buf` (the closure parameter)
396    let closure = generate_decode_closure(shape);
397    format!("({closure})(&buf)")
398}
399
400/// Get the Swift decode function name for a scalar type.
401pub fn swift_decode_fn(scalar: ScalarType) -> &'static str {
402    match scalar {
403        ScalarType::Bool => "decodeBool",
404        ScalarType::U8 => "decodeU8",
405        ScalarType::I8 => "decodeI8",
406        ScalarType::U16 => "decodeU16",
407        ScalarType::I16 => "decodeI16",
408        ScalarType::U32 => "decodeU32",
409        ScalarType::I32 => "decodeI32",
410        ScalarType::U64 | ScalarType::USize => "decodeVarint",
411        ScalarType::I64 | ScalarType::ISize => "decodeI64",
412        ScalarType::F32 => "decodeF32",
413        ScalarType::F64 => "decodeF64",
414        ScalarType::Char | ScalarType::Str | ScalarType::CowStr | ScalarType::String => {
415            "decodeString"
416        }
417        ScalarType::Unit => "{ _ in () }",
418        _ => "decodeBytes",
419    }
420}