roam_codegen/targets/typescript/
decode.rs

1//! TypeScript decoding statement generation.
2//!
3//! Generates TypeScript code that decodes byte arrays into Rust types.
4
5use facet_core::{ScalarType, Shape, StructKind};
6use roam_schema::{
7    EnumInfo, ShapeKind, StructInfo, VariantKind, classify_shape, classify_variant, is_bytes,
8};
9
10use super::types::{ts_type_base_named, ts_type_client_return, ts_type_server_arg};
11
12/// Generate TypeScript code that decodes a value from a buffer for CLIENT context.
13/// Schema is from server's perspective - types match on both sides.
14pub fn generate_decode_stmt_client(
15    shape: &'static Shape,
16    var_name: &str,
17    offset_var: &str,
18) -> String {
19    match classify_shape(shape) {
20        ShapeKind::Tx { inner } => {
21            // Caller's Tx (caller sends) - decode channel_id and create Tx handle
22            // r[impl channeling.type] - Channel types decode as channel_id on wire.
23            // TODO: Need Connection access to create proper Tx handle
24            let inner_type = ts_type_client_return(inner);
25            format!(
26                "const _{var_name}_r = decodeU64(buf, {offset_var}); const {var_name} = {{ channelId: _{var_name}_r.value }} as Tx<{inner_type}>; {offset_var} = _{var_name}_r.next; /* TODO: create real Tx handle */"
27            )
28        }
29        ShapeKind::Rx { inner } => {
30            // Caller's Rx (caller receives) - decode channel_id and create Rx handle
31            // r[impl channeling.type] - Channel types decode as channel_id on wire.
32            // TODO: Need Connection access to create proper Rx handle
33            let inner_type = ts_type_client_return(inner);
34            format!(
35                "const _{var_name}_r = decodeU64(buf, {offset_var}); const {var_name} = {{ channelId: _{var_name}_r.value }} as Rx<{inner_type}>; {offset_var} = _{var_name}_r.next; /* TODO: create real Rx handle */"
36            )
37        }
38        // For non-streaming types, use the regular decode
39        _ => generate_decode_stmt(shape, var_name, offset_var),
40    }
41}
42
43/// Generate TypeScript code that decodes a value from a buffer for SERVER context.
44/// Schema is from server's perspective - no inversion needed.
45/// - Schema Tx → server sends via Tx
46/// - Schema Rx → server receives via Rx
47pub fn generate_decode_stmt_server(
48    shape: &'static Shape,
49    var_name: &str,
50    offset_var: &str,
51) -> String {
52    match classify_shape(shape) {
53        ShapeKind::Tx { inner } => {
54            // Schema Tx → server sends via Tx
55            // r[impl channeling.type] - Channel types decode as channel_id on wire.
56            let inner_type = ts_type_server_arg(inner);
57            format!(
58                "const _{var_name}_r = decodeU64(buf, {offset_var}); const {var_name} = {{ channelId: _{var_name}_r.value }} as Tx<{inner_type}>; {offset_var} = _{var_name}_r.next; /* TODO: create real Tx handle */"
59            )
60        }
61        ShapeKind::Rx { inner } => {
62            // Schema Rx → server receives via Rx
63            // r[impl channeling.type] - Channel types decode as channel_id on wire.
64            let inner_type = ts_type_server_arg(inner);
65            format!(
66                "const _{var_name}_r = decodeU64(buf, {offset_var}); const {var_name} = {{ channelId: _{var_name}_r.value }} as Rx<{inner_type}>; {offset_var} = _{var_name}_r.next; /* TODO: create real Rx handle */"
67            )
68        }
69        // For non-streaming types, use the regular decode
70        _ => generate_decode_stmt(shape, var_name, offset_var),
71    }
72}
73
74/// Generate decode statement for server-side streaming context.
75/// Creates real Tx/Rx handles using the registry and taskSender.
76pub fn generate_decode_stmt_server_streaming(
77    shape: &'static Shape,
78    var_name: &str,
79    offset_var: &str,
80    registry_var: &str,
81    task_sender_var: &str,
82) -> String {
83    match classify_shape(shape) {
84        ShapeKind::Tx { inner } => {
85            // Server sends data to client via Tx
86            // Decode channel_id, create server-side Tx with taskSender
87            let inner_type = ts_type_server_arg(inner);
88            let encode_fn = super::encode::generate_encode_fn_inline(inner);
89            format!(
90                "const _{var_name}_r = decodeU64(buf, {offset_var}); \
91                 const {var_name} = createServerTx<{inner_type}>(_{var_name}_r.value, {task_sender_var}, {encode_fn}); \
92                 {offset_var} = _{var_name}_r.next;"
93            )
94        }
95        ShapeKind::Rx { inner } => {
96            // Server receives data from client via Rx
97            // Decode channel_id, register for incoming (creates channel), create Rx with receiver
98            let inner_type = ts_type_server_arg(inner);
99            let decode_fn = generate_decode_fn_inline(inner);
100            format!(
101                "const _{var_name}_r = decodeU64(buf, {offset_var}); \
102                 const _{var_name}_receiver = {registry_var}.registerIncoming(_{var_name}_r.value); \
103                 const {var_name} = createServerRx<{inner_type}>(_{var_name}_r.value, _{var_name}_receiver, {decode_fn}); \
104                 {offset_var} = _{var_name}_r.next;"
105            )
106        }
107        // For non-streaming types, use the regular decode
108        _ => generate_decode_stmt(shape, var_name, offset_var),
109    }
110}
111
112/// Generate TypeScript code that decodes a value from a buffer.
113/// Returns the decoded value in a variable and updates offset.
114/// `var_name` is the variable to assign the result to.
115/// `offset_var` is the variable holding the current offset.
116pub fn generate_decode_stmt(shape: &'static Shape, var_name: &str, offset_var: &str) -> String {
117    // Check for bytes first
118    if is_bytes(shape) {
119        return format!(
120            "const _{var_name}_r = decodeBytes(buf, {offset_var}); const {var_name} = _{var_name}_r.value; {offset_var} = _{var_name}_r.next;"
121        );
122    }
123
124    match classify_shape(shape) {
125        ShapeKind::Scalar(scalar) => decode_scalar_stmt(scalar, var_name, offset_var),
126        ShapeKind::List { element } => {
127            let decode_fn = generate_decode_fn(element, "item");
128            format!(
129                "const _{var_name}_r = decodeVec(buf, {offset_var}, {decode_fn}); const {var_name} = _{var_name}_r.value; {offset_var} = _{var_name}_r.next;"
130            )
131        }
132        ShapeKind::Option { inner } => {
133            let decode_fn = generate_decode_fn(inner, "inner");
134            format!(
135                "const _{var_name}_r = decodeOption(buf, {offset_var}, {decode_fn}); const {var_name} = _{var_name}_r.value; {offset_var} = _{var_name}_r.next;"
136            )
137        }
138        ShapeKind::Array { element, .. } | ShapeKind::Slice { element } => {
139            let decode_fn = generate_decode_fn(element, "item");
140            format!(
141                "const _{var_name}_r = decodeVec(buf, {offset_var}, {decode_fn}); const {var_name} = _{var_name}_r.value; {offset_var} = _{var_name}_r.next;"
142            )
143        }
144        ShapeKind::Tuple { elements } if elements.len() == 2 => {
145            let decode_a = generate_decode_fn(elements[0].shape, "a");
146            let decode_b = generate_decode_fn(elements[1].shape, "b");
147            format!(
148                "const _{var_name}_r = decodeTuple2(buf, {offset_var}, {decode_a}, {decode_b}); const {var_name} = _{var_name}_r.value; {offset_var} = _{var_name}_r.next;"
149            )
150        }
151        ShapeKind::Tuple { elements } if elements.len() == 3 => {
152            let decode_a = generate_decode_fn(elements[0].shape, "a");
153            let decode_b = generate_decode_fn(elements[1].shape, "b");
154            let decode_c = generate_decode_fn(elements[2].shape, "c");
155            format!(
156                "const _{var_name}_r = decodeTuple3(buf, {offset_var}, {decode_a}, {decode_b}, {decode_c}); const {var_name} = _{var_name}_r.value; {offset_var} = _{var_name}_r.next;"
157            )
158        }
159        ShapeKind::Tuple { elements } => {
160            if elements.is_empty() {
161                return format!("const {var_name} = undefined;");
162            }
163            // Generic tuple decoding
164            let mut code = format!("const {var_name}: [");
165            code.push_str(
166                &elements
167                    .iter()
168                    .map(|p| ts_type_base_named(p.shape))
169                    .collect::<Vec<_>>()
170                    .join(", "),
171            );
172            code.push_str("] = [] as any;\n");
173            for (i, param) in elements.iter().enumerate() {
174                let item_var = format!("{var_name}_{i}");
175                code.push_str(&generate_decode_stmt(param.shape, &item_var, offset_var));
176                code.push_str(&format!(" {var_name}[{i}] = {item_var};\n"));
177            }
178            code
179        }
180        ShapeKind::Struct(StructInfo { fields, kind, .. }) => {
181            if fields.is_empty() || kind == StructKind::Unit {
182                return format!("const {var_name} = undefined;");
183            }
184            let mut code = String::new();
185            for (i, field) in fields.iter().enumerate() {
186                let field_var = format!("{var_name}_f{i}");
187                code.push_str(&generate_decode_stmt(field.shape(), &field_var, offset_var));
188                code.push('\n');
189            }
190            code.push_str(&format!("const {var_name} = {{ "));
191            for (i, field) in fields.iter().enumerate() {
192                let field_var = format!("{var_name}_f{i}");
193                if i > 0 {
194                    code.push_str(", ");
195                }
196                code.push_str(&format!("{}: {field_var}", field.name));
197            }
198            code.push_str(" };");
199            code
200        }
201        ShapeKind::Enum(EnumInfo { variants, .. }) => {
202            let mut code = format!(
203                "const _{var_name}_disc = decodeEnumVariant(buf, {offset_var}); {offset_var} = _{var_name}_disc.next;\n"
204            );
205            code.push_str(&format!("let {var_name}: {};\n", ts_type_base_named(shape)));
206            code.push_str(&format!("switch (_{var_name}_disc.value) {{\n"));
207            for (i, v) in variants.iter().enumerate() {
208                code.push_str(&format!("  case {i}: {{\n"));
209                match classify_variant(v) {
210                    VariantKind::Unit => {
211                        code.push_str(&format!("    {var_name} = {{ tag: '{}' }};\n", v.name));
212                    }
213                    VariantKind::Newtype { inner } => {
214                        let inner_var = format!("{var_name}_inner");
215                        code.push_str(&format!(
216                            "    {}\n",
217                            generate_decode_stmt(inner, &inner_var, offset_var)
218                        ));
219                        code.push_str(&format!(
220                            "    {var_name} = {{ tag: '{}', value: {inner_var} }};\n",
221                            v.name
222                        ));
223                    }
224                    VariantKind::Tuple { fields } | VariantKind::Struct { fields } => {
225                        for (fi, field) in fields.iter().enumerate() {
226                            let field_var = format!("{var_name}_f{fi}");
227                            code.push_str(&format!(
228                                "    {}\n",
229                                generate_decode_stmt(field.shape(), &field_var, offset_var)
230                            ));
231                        }
232                        code.push_str(&format!("    {var_name} = {{ tag: '{}'", v.name));
233                        for (fi, field) in fields.iter().enumerate() {
234                            let field_var = format!("{var_name}_f{fi}");
235                            code.push_str(&format!(", {}: {field_var}", field.name));
236                        }
237                        code.push_str(" };\n");
238                    }
239                }
240                code.push_str("    break;\n  }\n");
241            }
242            code.push_str(&format!(
243                "  default: throw new Error(`unknown enum variant ${{_{var_name}_disc.value}}`);\n}}"
244            ));
245            code
246        }
247        ShapeKind::Map { key, value } => {
248            let decode_k = generate_decode_fn(key, "k");
249            let decode_v = generate_decode_fn(value, "v");
250            format!(
251                "const _{var_name}_r = decodeVec(buf, {offset_var}, (buf, off) => {{ \
252                const kr = ({decode_k})(buf, off); \
253                const vr = ({decode_v})(buf, kr.next); \
254                return {{ value: [kr.value, vr.value] as [any, any], next: vr.next }}; \
255                }}); const {var_name} = new Map(_{var_name}_r.value); {offset_var} = _{var_name}_r.next;"
256            )
257        }
258        ShapeKind::Set { element } => {
259            let decode_fn = generate_decode_fn(element, "item");
260            format!(
261                "const _{var_name}_r = decodeVec(buf, {offset_var}, {decode_fn}); const {var_name} = new Set(_{var_name}_r.value); {offset_var} = _{var_name}_r.next;"
262            )
263        }
264        ShapeKind::Tx { inner } => {
265            let inner_type = ts_type_base_named(inner);
266            format!(
267                "const _{var_name}_r = decodeU64(buf, {offset_var}); const {var_name} = {{ channelId: _{var_name}_r.value }} as Tx<{inner_type}>; {offset_var} = _{var_name}_r.next;"
268            )
269        }
270        ShapeKind::Rx { inner } => {
271            let inner_type = ts_type_base_named(inner);
272            format!(
273                "const _{var_name}_r = decodeU64(buf, {offset_var}); const {var_name} = {{ channelId: _{var_name}_r.value }} as Rx<{inner_type}>; {offset_var} = _{var_name}_r.next;"
274            )
275        }
276        ShapeKind::Pointer { pointee } => generate_decode_stmt(pointee, var_name, offset_var),
277        ShapeKind::Result { .. } => {
278            format!("const {var_name} = undefined; /* Result type decoding not yet implemented */")
279        }
280        ShapeKind::TupleStruct { fields } => {
281            let mut stmts = Vec::new();
282            for (i, f) in fields.iter().enumerate() {
283                stmts.push(generate_decode_stmt(
284                    f.shape(),
285                    &format!("{var_name}_{i}"),
286                    offset_var,
287                ));
288            }
289            let tuple_elements: Vec<String> = (0..fields.len())
290                .map(|i| format!("{var_name}_{i}"))
291                .collect();
292            // Generate tuple type for assertion
293            let tuple_types: Vec<String> = fields
294                .iter()
295                .map(|f| ts_type_base_named(f.shape()))
296                .collect();
297            stmts.push(format!(
298                "const {var_name} = [{}] as [{}];",
299                tuple_elements.join(", "),
300                tuple_types.join(", ")
301            ));
302            stmts.join("\n")
303        }
304        ShapeKind::Opaque => format!("const {var_name} = undefined; /* unsupported type */"),
305    }
306}
307
308/// Generate decode statement for scalar types.
309fn decode_scalar_stmt(scalar: ScalarType, var_name: &str, offset_var: &str) -> String {
310    match scalar {
311        ScalarType::Bool => format!(
312            "const _{var_name}_r = decodeBool(buf, {offset_var}); const {var_name} = _{var_name}_r.value; {offset_var} = _{var_name}_r.next;"
313        ),
314        ScalarType::U8 => format!(
315            "const _{var_name}_r = decodeU8(buf, {offset_var}); const {var_name} = _{var_name}_r.value; {offset_var} = _{var_name}_r.next;"
316        ),
317        ScalarType::I8 => format!(
318            "const _{var_name}_r = decodeI8(buf, {offset_var}); const {var_name} = _{var_name}_r.value; {offset_var} = _{var_name}_r.next;"
319        ),
320        ScalarType::U16 => format!(
321            "const _{var_name}_r = decodeU16(buf, {offset_var}); const {var_name} = _{var_name}_r.value; {offset_var} = _{var_name}_r.next;"
322        ),
323        ScalarType::I16 => format!(
324            "const _{var_name}_r = decodeI16(buf, {offset_var}); const {var_name} = _{var_name}_r.value; {offset_var} = _{var_name}_r.next;"
325        ),
326        ScalarType::U32 => format!(
327            "const _{var_name}_r = decodeU32(buf, {offset_var}); const {var_name} = _{var_name}_r.value; {offset_var} = _{var_name}_r.next;"
328        ),
329        ScalarType::I32 => format!(
330            "const _{var_name}_r = decodeI32(buf, {offset_var}); const {var_name} = _{var_name}_r.value; {offset_var} = _{var_name}_r.next;"
331        ),
332        ScalarType::U64 | ScalarType::USize => format!(
333            "const _{var_name}_r = decodeU64(buf, {offset_var}); const {var_name} = _{var_name}_r.value; {offset_var} = _{var_name}_r.next;"
334        ),
335        ScalarType::I64 | ScalarType::ISize => format!(
336            "const _{var_name}_r = decodeI64(buf, {offset_var}); const {var_name} = _{var_name}_r.value; {offset_var} = _{var_name}_r.next;"
337        ),
338        ScalarType::U128 => format!(
339            "const _{var_name}_r = decodeU64(buf, {offset_var}); const {var_name} = _{var_name}_r.value; {offset_var} = _{var_name}_r.next;"
340        ),
341        ScalarType::I128 => format!(
342            "const _{var_name}_r = decodeI64(buf, {offset_var}); const {var_name} = _{var_name}_r.value; {offset_var} = _{var_name}_r.next;"
343        ),
344        ScalarType::F32 => format!(
345            "const _{var_name}_r = decodeF32(buf, {offset_var}); const {var_name} = _{var_name}_r.value; {offset_var} = _{var_name}_r.next;"
346        ),
347        ScalarType::F64 => format!(
348            "const _{var_name}_r = decodeF64(buf, {offset_var}); const {var_name} = _{var_name}_r.value; {offset_var} = _{var_name}_r.next;"
349        ),
350        ScalarType::Char | ScalarType::Str | ScalarType::String | ScalarType::CowStr => format!(
351            "const _{var_name}_r = decodeString(buf, {offset_var}); const {var_name} = _{var_name}_r.value; {offset_var} = _{var_name}_r.next;"
352        ),
353        ScalarType::Unit => format!("const {var_name} = undefined;"),
354        _ => format!("const {var_name} = undefined; /* unsupported scalar */"),
355    }
356}
357
358/// Generate a decode function expression for use with decodeVec, decodeOption, etc.
359pub fn generate_decode_fn(shape: &'static Shape, _var_hint: &str) -> String {
360    // Check for bytes first
361    if is_bytes(shape) {
362        return "(buf, off) => decodeBytes(buf, off)".into();
363    }
364
365    match classify_shape(shape) {
366        ShapeKind::Scalar(scalar) => decode_scalar_fn(scalar),
367        ShapeKind::List { element } => {
368            let inner_fn = generate_decode_fn(element, "item");
369            format!("(buf, off) => decodeVec(buf, off, {inner_fn})")
370        }
371        ShapeKind::Option { inner } => {
372            let inner_fn = generate_decode_fn(inner, "inner");
373            format!("(buf, off) => decodeOption(buf, off, {inner_fn})")
374        }
375        ShapeKind::Array { element, .. } | ShapeKind::Slice { element } => {
376            let inner_fn = generate_decode_fn(element, "item");
377            format!("(buf, off) => decodeVec(buf, off, {inner_fn})")
378        }
379        ShapeKind::Tuple { elements } if elements.len() == 2 => {
380            let a_fn = generate_decode_fn(elements[0].shape, "a");
381            let b_fn = generate_decode_fn(elements[1].shape, "b");
382            format!("(buf, off) => decodeTuple2(buf, off, {a_fn}, {b_fn})")
383        }
384        ShapeKind::Tuple { elements } if elements.len() == 3 => {
385            let a_fn = generate_decode_fn(elements[0].shape, "a");
386            let b_fn = generate_decode_fn(elements[1].shape, "b");
387            let c_fn = generate_decode_fn(elements[2].shape, "c");
388            format!("(buf, off) => decodeTuple3(buf, off, {a_fn}, {b_fn}, {c_fn})")
389        }
390        ShapeKind::Tuple { elements: [] } => {
391            "(buf, off) => ({ value: undefined, next: off })".into()
392        }
393        ShapeKind::Struct(StructInfo { fields, kind, .. }) => {
394            if fields.is_empty() || kind == StructKind::Unit {
395                return "(buf, off) => ({ value: undefined, next: off })".into();
396            }
397            // Generate inline struct decoder
398            let mut code = "(buf: Uint8Array, off: number) => { let o = off;\n".to_string();
399            for (i, f) in fields.iter().enumerate() {
400                code.push_str(&format!(
401                    "  {}\n",
402                    generate_decode_stmt(f.shape(), &format!("f{i}"), "o")
403                ));
404            }
405            code.push_str("  return { value: { ");
406            for (i, f) in fields.iter().enumerate() {
407                if i > 0 {
408                    code.push_str(", ");
409                }
410                code.push_str(&format!("{}: f{i}", f.name));
411            }
412            code.push_str(" }, next: o };\n}");
413            code
414        }
415        ShapeKind::Enum(EnumInfo { variants, .. }) => {
416            // Generate inline enum decoder
417            let mut code =
418                "(buf: Uint8Array, off: number): DecodeResult<any> => { let o = off;\n".to_string();
419            code.push_str("  const disc = decodeEnumVariant(buf, o); o = disc.next;\n");
420            code.push_str("  switch (disc.value) {\n");
421            for (i, v) in variants.iter().enumerate() {
422                code.push_str(&format!("    case {i}: "));
423                match classify_variant(v) {
424                    VariantKind::Unit => {
425                        code.push_str(&format!(
426                            "return {{ value: {{ tag: '{}' }}, next: o }};\n",
427                            v.name
428                        ));
429                    }
430                    VariantKind::Newtype { inner } => {
431                        code.push_str("{\n");
432                        code.push_str(&format!(
433                            "      {}\n",
434                            generate_decode_stmt(inner, "val", "o")
435                        ));
436                        code.push_str(&format!(
437                            "      return {{ value: {{ tag: '{}', value: val }}, next: o }};\n",
438                            v.name
439                        ));
440                        code.push_str("    }\n");
441                    }
442                    VariantKind::Tuple { fields } | VariantKind::Struct { fields } => {
443                        code.push_str("{\n");
444                        for (j, f) in fields.iter().enumerate() {
445                            code.push_str(&format!(
446                                "      {}\n",
447                                generate_decode_stmt(f.shape(), &format!("f{j}"), "o")
448                            ));
449                        }
450                        code.push_str(&format!("      return {{ value: {{ tag: '{}', ", v.name));
451                        for (j, f) in fields.iter().enumerate() {
452                            if j > 0 {
453                                code.push_str(", ");
454                            }
455                            code.push_str(&format!("{}: f{j}", f.name));
456                        }
457                        code.push_str(" }, next: o };\n    }\n");
458                    }
459                }
460            }
461            code.push_str(
462                "    default: throw new Error(`unknown enum variant: ${disc.value}`);\n  }\n}",
463            );
464            code
465        }
466        ShapeKind::Pointer { pointee } => generate_decode_fn(pointee, _var_hint),
467        _ => "(buf, off) => { throw new Error('unsupported type'); }".into(),
468    }
469}
470
471/// Generate decode function for scalar types.
472fn decode_scalar_fn(scalar: ScalarType) -> String {
473    match scalar {
474        ScalarType::Bool => "(buf, off) => decodeBool(buf, off)".into(),
475        ScalarType::U8 => "(buf, off) => decodeU8(buf, off)".into(),
476        ScalarType::I8 => "(buf, off) => decodeI8(buf, off)".into(),
477        ScalarType::U16 => "(buf, off) => decodeU16(buf, off)".into(),
478        ScalarType::I16 => "(buf, off) => decodeI16(buf, off)".into(),
479        ScalarType::U32 => "(buf, off) => decodeU32(buf, off)".into(),
480        ScalarType::I32 => "(buf, off) => decodeI32(buf, off)".into(),
481        ScalarType::U64 | ScalarType::USize => "(buf, off) => decodeU64(buf, off)".into(),
482        ScalarType::I64 | ScalarType::ISize => "(buf, off) => decodeI64(buf, off)".into(),
483        ScalarType::U128 => "(buf, off) => decodeU64(buf, off)".into(),
484        ScalarType::I128 => "(buf, off) => decodeI64(buf, off)".into(),
485        ScalarType::F32 => "(buf, off) => decodeF32(buf, off)".into(),
486        ScalarType::F64 => "(buf, off) => decodeF64(buf, off)".into(),
487        ScalarType::Char | ScalarType::Str | ScalarType::String | ScalarType::CowStr => {
488            "(buf, off) => decodeString(buf, off)".into()
489        }
490        ScalarType::Unit => "(buf, off) => ({ value: undefined, next: off })".into(),
491        _ => "(buf, off) => { throw new Error('unsupported scalar'); }".into(),
492    }
493}
494
495/// Generate an inline decode function for a type.
496pub fn generate_decode_fn_inline(shape: &'static Shape) -> String {
497    // Check for bytes first
498    if is_bytes(shape) {
499        return "(bytes: Uint8Array) => bytes".into();
500    }
501
502    match classify_shape(shape) {
503        ShapeKind::Scalar(scalar) => decode_scalar_fn_inline(scalar),
504        _ => {
505            // For complex types, generate a function that decodes from offset 0
506            let decode_fn = generate_decode_fn(shape, "v");
507            format!("(bytes: Uint8Array) => ({decode_fn})(bytes, 0).value")
508        }
509    }
510}
511
512/// Generate inline decode function for scalars.
513fn decode_scalar_fn_inline(scalar: ScalarType) -> String {
514    match scalar {
515        ScalarType::Bool => "(bytes: Uint8Array) => decodeBool(bytes, 0).value".into(),
516        ScalarType::U8 => "(bytes: Uint8Array) => decodeU8(bytes, 0).value".into(),
517        ScalarType::I8 => "(bytes: Uint8Array) => decodeI8(bytes, 0).value".into(),
518        ScalarType::U16 => "(bytes: Uint8Array) => decodeU16(bytes, 0).value".into(),
519        ScalarType::I16 => "(bytes: Uint8Array) => decodeI16(bytes, 0).value".into(),
520        ScalarType::U32 => "(bytes: Uint8Array) => decodeU32(bytes, 0).value".into(),
521        ScalarType::I32 => "(bytes: Uint8Array) => decodeI32(bytes, 0).value".into(),
522        ScalarType::U64 | ScalarType::USize => {
523            "(bytes: Uint8Array) => decodeU64(bytes, 0).value".into()
524        }
525        ScalarType::I64 | ScalarType::ISize => {
526            "(bytes: Uint8Array) => decodeI64(bytes, 0).value".into()
527        }
528        ScalarType::F32 => "(bytes: Uint8Array) => decodeF32(bytes, 0).value".into(),
529        ScalarType::F64 => "(bytes: Uint8Array) => decodeF64(bytes, 0).value".into(),
530        ScalarType::Char | ScalarType::Str | ScalarType::String | ScalarType::CowStr => {
531            "(bytes: Uint8Array) => decodeString(bytes, 0).value".into()
532        }
533        ScalarType::Unit => "(bytes: Uint8Array) => undefined".into(),
534        _ => "(bytes: Uint8Array) => undefined".into(),
535    }
536}