Skip to main content

verit_core/
codegen.rs

1//! Rust code generation: compile a [`Schema`] into typed readers and writers.
2//!
3//! The generated reader is the novel part — an **identity fast path**:
4//! every message carries its writer-schema hash, so the generated `read()`
5//! compares it against the generation-time `SCHEMA_ID` constant. On a match
6//! (the overwhelmingly common case) every getter is a bounds-checked load at
7//! a **constant offset** computed here from the deterministic layout — the
8//! same code shape flatc emits. On a mismatch it transparently falls back to
9//! the resolver-driven plan path, keeping full schema evolution. Compile the
10//! happy path, interpret the evolution path.
11//!
12//! The generated writer is a direct single-pass encoder over typed `…Args`
13//! structs — no dynamic `Value` tree.
14//!
15//! Codegen v1 supports: all scalars, strings, bytes, enums (as raw `u32`),
16//! nested structs, `list<scalar>`, `list<string>`, `list<struct>`. Not yet:
17//! `list<bytes>`, `list<enum>`, nested lists (use the dynamic API for those).
18
19use std::collections::BTreeSet;
20use std::fmt::Write as _;
21
22use crate::error::{Error, Result};
23use crate::schema::{Schema, StructDef, Type, TypeDef};
24
25macro_rules! w {
26    ($out:expr, $($arg:tt)*) => { let _ = writeln!($out, $($arg)*); };
27}
28
29/// Generate a self-contained Rust module (as source text) for `schema`.
30/// Include it with `mod foo;` / `#[path = "..."] mod foo;` — it depends only
31/// on the `verit` crate's public API.
32pub fn generate_rust(schema: &Schema) -> Result<String> {
33    check_supported(schema)?;
34    let mut out = String::new();
35    w!(out, "// @generated by verit::codegen — do not edit.");
36    w!(out, "// schema id: {:#034x}", schema.id());
37    w!(out, "#![allow(dead_code, unused_imports, clippy::all)]");
38    w!(out,);
39    w!(out, "use verit::{{wire, Budget, Error, ListReader, Message, Ref, Resolver, Result, SchemaMode, StructReader}};");
40    w!(out,);
41    w!(out, "pub const SCHEMA_ID: u128 = {:#034x};", schema.id());
42    let bytes: Vec<String> = schema
43        .canonical_bytes()
44        .iter()
45        .map(|b| b.to_string())
46        .collect();
47    w!(
48        out,
49        "pub const SCHEMA_BYTES: &[u8] = &[{}];",
50        bytes.join(", ")
51    );
52    w!(out,);
53    w!(
54        out,
55        "/// The generated schema, decoded from its embedded canonical bytes."
56    );
57    w!(out, "pub fn schema() -> verit::Schema {{");
58    w!(
59        out,
60        "    verit::Schema::from_canonical(SCHEMA_BYTES).expect(\"embedded canonical schema\")"
61    );
62    w!(out, "}}");
63    w!(out,);
64    w!(out, "fn type_err(expected: &str, got: &str) -> Error {{");
65    w!(
66        out,
67        "    Error::TypeMismatch {{ expected: expected.into(), got: got.into() }}"
68    );
69    w!(out, "}}");
70
71    // Scalar/string list helper enums, one per element kind in use.
72    let mut scalar_lists: BTreeSet<&'static str> = BTreeSet::new();
73    let mut str_list = false;
74    for idx in 0..schema.type_count() {
75        if let Some(TypeDef::Struct(sd)) = schema.type_def(idx) {
76            for f in &sd.fields {
77                if let Type::List(elem) = &f.ty {
78                    match elem.as_ref() {
79                        Type::String => str_list = true,
80                        Type::Struct(_) => {}
81                        other => {
82                            if let Some(info) = scalar_info(other) {
83                                scalar_lists.insert(info.rust);
84                            }
85                        }
86                    }
87                }
88            }
89        }
90    }
91    for rust in &scalar_lists {
92        emit_scalar_list(&mut out, rust);
93    }
94    if str_list {
95        emit_str_list(&mut out);
96    }
97
98    for idx in 0..schema.type_count() {
99        if let Some(TypeDef::Struct(sd)) = schema.type_def(idx) {
100            emit_struct_reader(&mut out, schema, idx, sd)?;
101            emit_struct_writer(&mut out, schema, idx, sd)?;
102        }
103    }
104    Ok(out)
105}
106
107// ---------------------------------------------------------------------------
108// Support / naming checks
109// ---------------------------------------------------------------------------
110
111/// Does this type contain a `map` or `union` anywhere? Codegen (all languages)
112/// does not yet support them; a schema using one must be read through the
113/// dynamic API.
114fn type_has_map(ty: &Type) -> bool {
115    match ty {
116        Type::Map(_, _) | Type::Union(_) => true,
117        Type::List(e) => type_has_map(e),
118        _ => false,
119    }
120}
121
122/// Reject any schema whose fields use `map`/`union` — shared by every emitter
123/// until that codegen lands across the ports.
124fn reject_maps(schema: &Schema) -> Result<()> {
125    for idx in 0..schema.type_count() {
126        if let Some(TypeDef::Struct(sd)) = schema.type_def(idx) {
127            for f in &sd.fields {
128                if type_has_map(&f.ty) {
129                    return Err(Error::BadSchema(format!(
130                        "codegen does not yet support map/union types (field {} of {}); \
131                         read them through the dynamic API",
132                        f.name, sd.name
133                    )));
134                }
135            }
136        }
137    }
138    Ok(())
139}
140
141fn check_supported(schema: &Schema) -> Result<()> {
142    reject_maps(schema)?;
143    for idx in 0..schema.type_count() {
144        let td = schema.type_def(idx).unwrap();
145        check_ident(td.name())?;
146        if let TypeDef::Struct(sd) = td {
147            if sd.is_packed() {
148                return Err(Error::BadSchema(format!(
149                    "codegen v1 does not support packed struct {}: packed layout \
150                     has per-message dynamic offsets — use the dynamic reader API",
151                    sd.name
152                )));
153            }
154            for f in &sd.fields {
155                check_ident(&f.name)?;
156                if let Type::List(elem) = &f.ty {
157                    match elem.as_ref() {
158                        Type::Bytes | Type::Enum(_) | Type::List(_) => {
159                            return Err(Error::BadSchema(format!(
160                                "codegen v1 does not support field {} of {}: \
161                                 list<bytes>, list<enum>, and nested lists need the dynamic API",
162                                f.name, sd.name
163                            )))
164                        }
165                        _ => {}
166                    }
167                }
168            }
169        }
170    }
171    Ok(())
172}
173
174const KEYWORDS: &[&str] = &[
175    "as", "async", "await", "box", "break", "const", "continue", "crate", "dyn", "else", "enum",
176    "extern", "false", "fn", "for", "if", "impl", "in", "let", "loop", "match", "mod", "move",
177    "mut", "pub", "ref", "return", "self", "static", "struct", "super", "trait", "true", "type",
178    "unsafe", "use", "where", "while",
179];
180
181fn check_ident(name: &str) -> Result<()> {
182    let mut chars = name.chars();
183    let ok = matches!(chars.next(), Some(c) if c.is_ascii_alphabetic() || c == '_')
184        && chars.all(|c| c.is_ascii_alphanumeric() || c == '_')
185        && !KEYWORDS.contains(&name);
186    if ok {
187        Ok(())
188    } else {
189        Err(Error::BadSchema(format!(
190            "name {name:?} is not a usable Rust identifier for codegen"
191        )))
192    }
193}
194
195fn snake(name: &str) -> String {
196    let mut s = String::with_capacity(name.len() + 4);
197    for (i, c) in name.chars().enumerate() {
198        if c.is_ascii_uppercase() {
199            if i > 0 {
200                s.push('_');
201            }
202            s.push(c.to_ascii_lowercase());
203        } else {
204            s.push(c);
205        }
206    }
207    s
208}
209
210// ---------------------------------------------------------------------------
211// Scalar table
212// ---------------------------------------------------------------------------
213
214struct ScalarInfo {
215    rust: &'static str,
216    read: &'static str,
217    put: &'static str,
218    push_slice: &'static str,
219    dyn_get: &'static str,
220    size: u32,
221    align: u32,
222}
223
224fn scalar_info(ty: &Type) -> Option<ScalarInfo> {
225    macro_rules! s {
226        ($rust:literal, $suffix:literal, $n:literal) => {
227            Some(ScalarInfo {
228                rust: $rust,
229                read: concat!("read_", $suffix),
230                put: concat!("put_", $suffix),
231                push_slice: concat!("push_", $suffix, "_slice"),
232                dyn_get: concat!("get_", $suffix),
233                size: $n,
234                align: $n,
235            })
236        };
237    }
238    match ty {
239        Type::Bool => s!("bool", "bool", 1),
240        Type::U8 => s!("u8", "u8", 1),
241        Type::U16 => s!("u16", "u16", 2),
242        Type::U32 => s!("u32", "u32", 4),
243        Type::U64 => s!("u64", "u64", 8),
244        Type::I8 => s!("i8", "i8", 1),
245        Type::I16 => s!("i16", "i16", 2),
246        Type::I32 => s!("i32", "i32", 4),
247        Type::I64 => s!("i64", "i64", 8),
248        Type::F32 => s!("f32", "f32", 4),
249        Type::F64 => s!("f64", "f64", 8),
250        Type::Enum(_) => Some(ScalarInfo {
251            rust: "u32",
252            read: "read_u32",
253            put: "put_u32",
254            push_slice: "push_u32_slice",
255            dyn_get: "get_enum",
256            size: 4,
257            align: 4,
258        }),
259        _ => None,
260    }
261}
262
263fn list_ref_type(schema: &Schema, elem: &Type) -> String {
264    match elem {
265        Type::Struct(i) => format!("{}List", schema.type_name(*i)),
266        Type::String => "StrList".to_string(),
267        other => {
268            let info = scalar_info(other).expect("checked supported");
269            format!("{}List", pascal(info.rust))
270        }
271    }
272}
273
274fn pascal(s: &str) -> String {
275    let mut c = s.chars();
276    match c.next() {
277        Some(first) => first.to_ascii_uppercase().to_string() + c.as_str(),
278        None => String::new(),
279    }
280}
281
282/// Does this struct's Args type borrow anything (need a lifetime)?
283fn args_needs_lifetime(schema: &Schema, idx: u16) -> bool {
284    let sd = match schema.type_def(idx) {
285        Some(TypeDef::Struct(s)) => s,
286        _ => return false,
287    };
288    sd.fields.iter().any(|f| scalar_info(&f.ty).is_none())
289}
290
291// ---------------------------------------------------------------------------
292// Reader emission
293// ---------------------------------------------------------------------------
294
295fn emit_scalar_list(out: &mut String, rust: &'static str) {
296    let name = format!("{}List", pascal(rust));
297    let info = scalar_info(&rust_to_type(rust)).unwrap();
298    let variant = pascal(rust);
299    w!(out,);
300    w!(out, "#[derive(Clone)]");
301    w!(out, "pub enum {name}<'b, 'r> {{");
302    w!(
303        out,
304        "    // On a bounded read the whole element region was charged when the"
305    );
306    w!(
307        out,
308        "    // list was opened, so element access here is charge-free."
309    );
310    w!(out, "    Fast {{ buf: &'b [u8], elems: u64, count: u32 }},");
311    w!(out, "    Dynamic(ListReader<'b, 'r>),");
312    w!(out, "}}");
313    w!(out,);
314    w!(out, "impl<'b, 'r> {name}<'b, 'r> {{");
315    w!(out, "    pub fn len(&self) -> u32 {{");
316    w!(out, "        match self {{ Self::Fast {{ count, .. }} => *count, Self::Dynamic(l) => l.len() }}");
317    w!(out, "    }}");
318    w!(
319        out,
320        "    pub fn is_empty(&self) -> bool {{ self.len() == 0 }}"
321    );
322    w!(out, "    pub fn get(&self, i: u32) -> Result<{rust}> {{");
323    w!(out, "        match self {{");
324    w!(out, "            Self::Fast {{ buf, elems, count }} => {{");
325    w!(
326        out,
327        "                if i >= *count {{ return Err(Error::IndexOutOfBounds); }}"
328    );
329    w!(
330        out,
331        "                wire::{}(*buf, *elems + i as u64 * {})",
332        info.read,
333        info.size
334    );
335    w!(out, "            }}");
336    w!(out, "            Self::Dynamic(l) => match l.get(i)? {{");
337    if rust == "u32" {
338        // Both Ref::U32 and Ref::Enum surface as u32 here.
339        w!(out, "                Ref::U32(v) => Ok(v),");
340        w!(out, "                Ref::Enum(v) => Ok(v),");
341    } else {
342        w!(out, "                Ref::{variant}(v) => Ok(v),");
343    }
344    w!(
345        out,
346        "                other => Err(type_err(\"{rust}\", other.kind())),"
347    );
348    w!(out, "            }},");
349    w!(out, "        }}");
350    w!(out, "    }}");
351    // Fast bulk scan: on the identity path validate the whole element region
352    // once, then read each element with no per-element bounds check and no
353    // Result wrapping (`chunks_exact`), so the loop autovectorizes. `None` on
354    // the evolved-schema path (use `get`/`iter`) or a corrupt element region.
355    let decode = if rust == "bool" {
356        "|c| c[0] != 0".to_string()
357    } else {
358        format!("|c| {rust}::from_le_bytes(c.try_into().unwrap())")
359    };
360    w!(
361        out,
362        "    pub fn values(&self) -> Option<impl Iterator<Item = {rust}> + 'b> {{"
363    );
364    w!(out, "        match self {{");
365    w!(out, "            Self::Fast {{ buf, elems, count }} => {{");
366    w!(out, "                let start = *elems as usize;");
367    w!(out, "                (*count as usize)");
368    w!(out, "                    .checked_mul({})", info.size);
369    w!(
370        out,
371        "                    .and_then(|n| start.checked_add(n))"
372    );
373    w!(
374        out,
375        "                    .and_then(|end| buf.get(start..end))"
376    );
377    w!(
378        out,
379        "                    .map(|region| region.chunks_exact({}).map({decode}))",
380        info.size
381    );
382    w!(out, "            }}");
383    w!(out, "            Self::Dynamic(_) => None,");
384    w!(out, "        }}");
385    w!(out, "    }}");
386    w!(
387        out,
388        "    /// General fallible iterator (fast + dynamic). Prefer `values()`"
389    );
390    w!(out, "    /// for a hot scan on the identity path.");
391    w!(
392        out,
393        "    pub fn iter(&self) -> impl Iterator<Item = Result<{rust}>> + '_ {{"
394    );
395    w!(out, "        (0..self.len()).map(move |i| self.get(i))");
396    w!(out, "    }}");
397    w!(out, "}}");
398}
399
400fn rust_to_type(rust: &str) -> Type {
401    match rust {
402        "bool" => Type::Bool,
403        "u8" => Type::U8,
404        "u16" => Type::U16,
405        "u32" => Type::U32,
406        "u64" => Type::U64,
407        "i8" => Type::I8,
408        "i16" => Type::I16,
409        "i32" => Type::I32,
410        "i64" => Type::I64,
411        "f32" => Type::F32,
412        "f64" => Type::F64,
413        _ => unreachable!("scalar rust type"),
414    }
415}
416
417fn emit_str_list(out: &mut String) {
418    w!(out,);
419    w!(out, "#[derive(Clone)]");
420    w!(out, "pub enum StrList<'b, 'r> {{");
421    w!(
422        out,
423        "    // String elements follow per-element offsets to payloads, so each"
424    );
425    w!(
426        out,
427        "    // `get` charges its payload against the budget on a bounded read"
428    );
429    w!(
430        out,
431        "    // (aliased element offsets recharge on every visit)."
432    );
433    w!(
434        out,
435        "    Fast {{ buf: &'b [u8], elems: u64, count: u32, budget: Option<&'r Budget> }},"
436    );
437    w!(out, "    Dynamic(ListReader<'b, 'r>),");
438    w!(out, "}}");
439    w!(out,);
440    w!(out, "impl<'b, 'r> StrList<'b, 'r> {{");
441    w!(out, "    pub fn len(&self) -> u32 {{");
442    w!(out, "        match self {{ Self::Fast {{ count, .. }} => *count, Self::Dynamic(l) => l.len() }}");
443    w!(out, "    }}");
444    w!(
445        out,
446        "    pub fn is_empty(&self) -> bool {{ self.len() == 0 }}"
447    );
448    w!(out, "    pub fn get(&self, i: u32) -> Result<&'b str> {{");
449    w!(out, "        match self {{");
450    w!(
451        out,
452        "            Self::Fast {{ buf, elems, count, budget }} => {{"
453    );
454    w!(
455        out,
456        "                if i >= *count {{ return Err(Error::IndexOutOfBounds); }}"
457    );
458    w!(
459        out,
460        "                wire::read_str_budgeted(*buf, *elems + i as u64 * 4, *budget)"
461    );
462    w!(out, "            }}");
463    w!(out, "            Self::Dynamic(l) => match l.get(i)? {{");
464    w!(out, "                Ref::Str(v) => Ok(v),");
465    w!(
466        out,
467        "                other => Err(type_err(\"string\", other.kind())),"
468    );
469    w!(out, "            }},");
470    w!(out, "        }}");
471    w!(out, "    }}");
472    w!(out, "}}");
473}
474
475fn emit_struct_reader(out: &mut String, schema: &Schema, idx: u16, sd: &StructDef) -> Result<()> {
476    let name = &sd.name;
477    let lay = schema.layout_unchecked(idx).as_fixed();
478    let is_root = idx == schema.root_index();
479
480    // The struct-list companion, if this struct appears as a list element.
481    let used_in_list = struct_used_in_list(schema, idx);
482
483    w!(out,);
484    w!(out, "#[derive(Clone)]");
485    w!(out, "pub enum {name}Ref<'b, 'r> {{");
486    w!(
487        out,
488        "    // `budget` is Some on a bounded (untrusted-input) read: offset-follows"
489    );
490    w!(
491        out,
492        "    // — string/bytes payloads, nested blocks, list regions — charge it, so"
493    );
494    w!(
495        out,
496        "    // aliased offsets can't amplify work (the wire spec §5.2). None = trusted,"
497    );
498    w!(
499        out,
500        "    // zero-cost. Reads within an already-charged block are charge-free."
501    );
502    w!(
503        out,
504        "    Fast {{ buf: &'b [u8], base: u64, budget: Option<&'r Budget> }},"
505    );
506    w!(out, "    Dynamic(StructReader<'b, 'r>),");
507    w!(out, "}}");
508    w!(out,);
509    w!(out, "impl<'b, 'r> {name}Ref<'b, 'r> {{");
510    if is_root {
511        w!(
512            out,
513            "    /// Open the root struct. Takes the identity fast path (constant"
514        );
515        w!(
516            out,
517            "    /// offsets, resolver untouched) when the message was written with"
518        );
519        w!(
520            out,
521            "    /// exactly this generated schema; otherwise falls back to the"
522        );
523        w!(
524            out,
525            "    /// resolver's access plans, keeping full schema evolution."
526        );
527        w!(
528            out,
529            "    /// **Unbounded** (no traversal-work cap): for trusted input, or"
530        );
531        w!(
532            out,
533            "    /// untrusted input behind [`Self::read_bounded`] / an upstream size cap."
534        );
535        w!(
536            out,
537            "    pub fn read(msg: &Message<'b>, resolver: &'r Resolver) -> Result<Self> {{"
538        );
539        w!(out, "        if msg.schema_id() == SCHEMA_ID {{");
540        w!(out, "            Ok(Self::Fast {{ buf: msg.buffer(), base: msg.root_offset() as u64, budget: None }})");
541        w!(out, "        }} else {{");
542        w!(out, "            Ok(Self::Dynamic(msg.root(resolver)?))");
543        w!(out, "        }}");
544        w!(out, "    }}");
545        w!(
546            out,
547            "    /// Like [`Self::read`], but every offset-follow charges `budget`, so a"
548        );
549        w!(
550            out,
551            "    /// crafted offset-aliasing message trips `TraversalBudgetExceeded`"
552        );
553        w!(
554            out,
555            "    /// instead of amplifying work (the wire spec §5.2). Fast-path getters stay"
556        );
557        w!(
558            out,
559            "    /// constant-offset loads; the only added cost is one budget decrement"
560        );
561        w!(
562            out,
563            "    /// per heap follow. Pair with `Message::suggested_budget()`."
564        );
565        w!(out, "    pub fn read_bounded(");
566        w!(out, "        msg: &Message<'b>,");
567        w!(out, "        resolver: &'r Resolver,");
568        w!(out, "        budget: &'r Budget,");
569        w!(out, "    ) -> Result<Self> {{");
570        w!(out, "        if msg.schema_id() == SCHEMA_ID {{");
571        w!(
572            out,
573            "            budget.charge({})?; // root block",
574            lay.size
575        );
576        w!(out, "            Ok(Self::Fast {{ buf: msg.buffer(), base: msg.root_offset() as u64, budget: Some(budget) }})");
577        w!(out, "        }} else {{");
578        w!(
579            out,
580            "            Ok(Self::Dynamic(msg.root_bounded(resolver, budget)?))"
581        );
582        w!(out, "        }}");
583        w!(out, "    }}");
584    }
585    for (pos, f) in sd.fields.iter().enumerate() {
586        let slot = lay.slots[pos];
587        let fname = &f.name;
588        let id = f.id;
589        // Presence guard for the fast arm (sparse structs only).
590        let guard = if sd.is_dense() {
591            String::new()
592        } else {
593            format!(
594                "if wire::read_u8(*buf, *base + {})? & {} == 0 {{ return Ok(None); }}\n                ",
595                pos / 8,
596                1u8 << (pos % 8)
597            )
598        };
599        match &f.ty {
600            Type::String => {
601                w!(
602                    out,
603                    "    pub fn {fname}(&self) -> Result<Option<&'b str>> {{"
604                );
605                w!(out, "        match self {{");
606                w!(out, "            Self::Fast {{ buf, base, budget }} => {{");
607                w!(out, "                {guard}Ok(Some(wire::read_str_budgeted(*buf, *base + {slot}, *budget)?))");
608                w!(out, "            }}");
609                w!(out, "            Self::Dynamic(r) => r.get_str({id}),");
610                w!(out, "        }}");
611                w!(out, "    }}");
612            }
613            Type::Bytes => {
614                w!(
615                    out,
616                    "    pub fn {fname}(&self) -> Result<Option<&'b [u8]>> {{"
617                );
618                w!(out, "        match self {{");
619                w!(out, "            Self::Fast {{ buf, base, budget }} => {{");
620                w!(out, "                {guard}Ok(Some(wire::read_bytes_budgeted(*buf, *base + {slot}, *budget)?))");
621                w!(out, "            }}");
622                w!(out, "            Self::Dynamic(r) => r.get_bytes({id}),");
623                w!(out, "        }}");
624                w!(out, "    }}");
625            }
626            Type::Struct(ci) => {
627                let cname = schema.type_name(*ci);
628                let csize = schema.layout_unchecked(*ci).as_fixed().size;
629                w!(
630                    out,
631                    "    pub fn {fname}(&self) -> Result<Option<{cname}Ref<'b, 'r>>> {{"
632                );
633                w!(out, "        match self {{");
634                w!(out, "            Self::Fast {{ buf, base, budget }} => {{");
635                w!(out, "                {guard}let off = wire::read_u32(*buf, *base + {slot})? as u64;");
636                w!(
637                    out,
638                    "                wire::charge(*budget, {csize})?; // child block"
639                );
640                w!(out, "                Ok(Some({cname}Ref::Fast {{ buf: *buf, base: off, budget: *budget }}))");
641                w!(out, "            }}");
642                w!(out, "            Self::Dynamic(r) => Ok(r.get_struct({id})?.map({cname}Ref::Dynamic)),");
643                w!(out, "        }}");
644                w!(out, "    }}");
645            }
646            Type::List(elem) => {
647                let lty = list_ref_type(schema, elem);
648                let (estride, ealign) = elem_stride_align(schema, elem);
649                // Str and struct lists carry the budget on (their elements
650                // follow further offsets); scalar lists don't need it — their
651                // whole region is charged here at open.
652                let ctor_budget = match elem.as_ref() {
653                    Type::String | Type::Struct(_) => ", budget: *budget",
654                    _ => "",
655                };
656                w!(
657                    out,
658                    "    pub fn {fname}(&self) -> Result<Option<{lty}<'b, 'r>>> {{"
659                );
660                w!(out, "        match self {{");
661                w!(out, "            Self::Fast {{ buf, base, budget }} => {{");
662                w!(out, "                {guard}let (elems, count) = wire::list_header(*buf, *base + {slot}, {ealign})?;");
663                w!(
664                    out,
665                    "                // Charge the element region once at open; element access"
666                );
667                w!(out, "                // within it is then charge-free (a hostile count trips here).");
668                w!(
669                    out,
670                    "                wire::charge(*budget, 4 + count as u64 * {estride})?;"
671                );
672                w!(out, "                Ok(Some({lty}::Fast {{ buf: *buf, elems, count{ctor_budget} }}))");
673                w!(out, "            }}");
674                w!(
675                    out,
676                    "            Self::Dynamic(r) => Ok(r.get_list({id})?.map({lty}::Dynamic)),"
677                );
678                w!(out, "        }}");
679                w!(out, "    }}");
680            }
681            other => {
682                let info = scalar_info(other).expect("scalar");
683                let rust = info.rust;
684                let (read, dyn_get) = (info.read, info.dyn_get);
685                w!(
686                    out,
687                    "    pub fn {fname}(&self) -> Result<Option<{rust}>> {{"
688                );
689                w!(out, "        match self {{");
690                w!(out, "            Self::Fast {{ buf, base, .. }} => {{");
691                w!(
692                    out,
693                    "                {guard}Ok(Some(wire::{read}(*buf, *base + {slot})?))"
694                );
695                w!(out, "            }}");
696                w!(out, "            Self::Dynamic(r) => r.{dyn_get}({id}),");
697                w!(out, "        }}");
698                w!(out, "    }}");
699            }
700        }
701    }
702    w!(out, "}}");
703
704    if used_in_list {
705        let (stride, _) = struct_stride_align(schema, idx);
706        w!(out,);
707        w!(out, "#[derive(Clone)]");
708        w!(out, "pub enum {name}List<'b, 'r> {{");
709        w!(
710            out,
711            "    // The element region was charged when the list was opened; the budget"
712        );
713        w!(
714            out,
715            "    // rides along so element refs can charge *their* offset-follows."
716        );
717        w!(
718            out,
719            "    Fast {{ buf: &'b [u8], elems: u64, count: u32, budget: Option<&'r Budget> }},"
720        );
721        w!(out, "    Dynamic(ListReader<'b, 'r>),");
722        w!(out, "}}");
723        w!(out,);
724        w!(out, "impl<'b, 'r> {name}List<'b, 'r> {{");
725        w!(out, "    pub fn len(&self) -> u32 {{");
726        w!(out, "        match self {{ Self::Fast {{ count, .. }} => *count, Self::Dynamic(l) => l.len() }}");
727        w!(out, "    }}");
728        w!(
729            out,
730            "    pub fn is_empty(&self) -> bool {{ self.len() == 0 }}"
731        );
732        w!(
733            out,
734            "    pub fn get(&self, i: u32) -> Result<{name}Ref<'b, 'r>> {{"
735        );
736        w!(out, "        match self {{");
737        w!(
738            out,
739            "            Self::Fast {{ buf, elems, count, budget }} => {{"
740        );
741        w!(
742            out,
743            "                if i >= *count {{ return Err(Error::IndexOutOfBounds); }}"
744        );
745        w!(out, "                Ok({name}Ref::Fast {{ buf: *buf, base: *elems + i as u64 * {stride}, budget: *budget }})");
746        w!(out, "            }}");
747        w!(out, "            Self::Dynamic(l) => match l.get(i)? {{");
748        w!(
749            out,
750            "                Ref::Struct(s) => Ok({name}Ref::Dynamic(s)),"
751        );
752        w!(
753            out,
754            "                other => Err(type_err(\"struct\", other.kind())),"
755        );
756        w!(out, "            }},");
757        w!(out, "        }}");
758        w!(out, "    }}");
759
760        // A struct whose fields are all fixed scalars/enums (no heap refs to
761        // follow) supports a fast bulk scan: each element is a `…Block` over
762        // its `&[u8; SIZE]` with infallible constant-offset getters, so the
763        // scan validates the region once and then autovectorizes.
764        let all_fixed = sd.fields.iter().all(|f| scalar_info(&f.ty).is_some());
765        if all_fixed {
766            w!(
767                out,
768                "    /// Fast bulk scan on the identity path: validate the element region"
769            );
770            w!(
771                out,
772                "    /// once, then yield infallible `{name}Block`s. `None` on the evolved"
773            );
774            w!(out, "    /// path (use `get`/`iter`) or a corrupt region.");
775            w!(
776                out,
777                "    pub fn blocks(&self) -> Option<impl Iterator<Item = {name}Block<'b>> + 'b> {{"
778            );
779            w!(out, "        match self {{");
780            w!(
781                out,
782                "            Self::Fast {{ buf, elems, count, .. }} => {{"
783            );
784            w!(out, "                let start = *elems as usize;");
785            w!(out, "                (*count as usize)");
786            w!(out, "                    .checked_mul({stride})");
787            w!(
788                out,
789                "                    .and_then(|n| start.checked_add(n))"
790            );
791            w!(
792                out,
793                "                    .and_then(|end| buf.get(start..end))"
794            );
795            w!(out, "                    .map(|region| region.chunks_exact({stride}).map(|c| {name}Block(c.try_into().unwrap())))");
796            w!(out, "            }}");
797            w!(out, "            Self::Dynamic(_) => None,");
798            w!(out, "        }}");
799            w!(out, "    }}");
800        }
801        w!(
802            out,
803            "    /// General fallible iterator (fast + dynamic). Prefer `blocks()` for a"
804        );
805        w!(out, "    /// hot scan on the identity path.");
806        w!(
807            out,
808            "    pub fn iter(&self) -> impl Iterator<Item = Result<{name}Ref<'b, 'r>>> + '_ {{"
809        );
810        w!(out, "        (0..self.len()).map(move |i| self.get(i))");
811        w!(out, "    }}");
812        w!(out, "}}");
813
814        if all_fixed {
815            emit_struct_block(out, schema, idx, sd, stride);
816        }
817    }
818    Ok(())
819}
820
821/// Emit `…Block<'b>` — a `&'b [u8; SIZE]` view of one fixed struct element
822/// whose whole block was bounds-checked once by `…List::blocks()`. Field
823/// getters read at constant offsets on the sized array, so the compiler proves
824/// them in-bounds and elides every check; they are infallible (dense) or
825/// `Option` (sparse, presence-bit checked) — never `Result`.
826fn emit_struct_block(out: &mut String, schema: &Schema, idx: u16, sd: &StructDef, stride: u32) {
827    let name = &sd.name;
828    let lay = schema.layout_unchecked(idx).as_fixed();
829    w!(out,);
830    w!(out, "#[derive(Clone, Copy)]");
831    w!(out, "pub struct {name}Block<'b>(&'b [u8; {stride}]);");
832    w!(out,);
833    w!(out, "impl<'b> {name}Block<'b> {{");
834    for (pos, f) in sd.fields.iter().enumerate() {
835        let info = scalar_info(&f.ty).expect("all_fixed");
836        let rust = info.rust;
837        let slot = lay.slots[pos] as usize;
838        let sz = info.size as usize;
839        // Constant-offset read from the sized array (checks provably elided).
840        let read = if rust == "bool" {
841            format!("self.0[{slot}] != 0")
842        } else if sz == 1 {
843            format!("self.0[{slot}] as {rust}")
844        } else {
845            format!(
846                "{rust}::from_le_bytes(self.0[{slot}..{}].try_into().unwrap())",
847                slot + sz
848            )
849        };
850        let fname = &f.name;
851        if sd.is_dense() {
852            w!(out, "    pub fn {fname}(&self) -> {rust} {{ {read} }}");
853        } else {
854            let (byte, mask) = (pos / 8, 1u8 << (pos % 8));
855            w!(out, "    pub fn {fname}(&self) -> Option<{rust}> {{");
856            w!(
857                out,
858                "        if self.0[{byte}] & {mask} == 0 {{ return None; }}"
859            );
860            w!(out, "        Some({read})");
861            w!(out, "    }}");
862        }
863    }
864    w!(out, "}}");
865}
866
867fn struct_used_in_list(schema: &Schema, idx: u16) -> bool {
868    for i in 0..schema.type_count() {
869        if let Some(TypeDef::Struct(sd)) = schema.type_def(i) {
870            for f in &sd.fields {
871                if matches!(&f.ty, Type::List(e) if matches!(e.as_ref(), Type::Struct(ci) if *ci == idx))
872                {
873                    return true;
874                }
875            }
876        }
877    }
878    false
879}
880
881fn struct_stride_align(schema: &Schema, idx: u16) -> (u32, u32) {
882    let lay = schema.layout_unchecked(idx).as_fixed();
883    (lay.size, lay.align)
884}
885
886fn elem_stride_align(schema: &Schema, elem: &Type) -> (u32, u32) {
887    match elem {
888        Type::Struct(i) => struct_stride_align(schema, *i),
889        Type::String => (4, 4),
890        other => {
891            let info = scalar_info(other).expect("scalar");
892            (info.size, info.align)
893        }
894    }
895}
896
897// ---------------------------------------------------------------------------
898// Writer emission
899// ---------------------------------------------------------------------------
900
901fn emit_struct_writer(out: &mut String, schema: &Schema, idx: u16, sd: &StructDef) -> Result<()> {
902    let name = &sd.name;
903    let fn_name = snake(name);
904    let lay = schema.layout_unchecked(idx).as_fixed();
905    let needs_lt = args_needs_lifetime(schema, idx);
906    let lt_decl = if needs_lt { "<'a>" } else { "" };
907    let is_root = idx == schema.root_index();
908
909    // Args struct.
910    w!(out,);
911    if sd.is_dense() {
912        w!(
913            out,
914            "/// Typed writer args for dense struct `{name}` (all fields required)."
915        );
916    } else {
917        w!(
918            out,
919            "/// Typed writer args for `{name}` (`None` = field absent)."
920        );
921    }
922    w!(out, "pub struct {name}Args{lt_decl} {{");
923    for f in &sd.fields {
924        let fty = args_field_type(schema, &f.ty, sd.is_dense());
925        w!(out, "    pub {}: {},", f.name, fty);
926    }
927    w!(out, "}}");
928
929    // fill fn.
930    let args_ty = if needs_lt {
931        format!("{name}Args<'_>")
932    } else {
933        format!("{name}Args")
934    };
935    w!(out,);
936    w!(
937        out,
938        "fn fill_{fn_name}_at(buf: &mut Vec<u8>, base: u32, args: &{args_ty}) -> Result<()> {{"
939    );
940    for (pos, f) in sd.fields.iter().enumerate() {
941        let slot = lay.slots[pos];
942        let (open, val, indent, close) = if sd.is_dense() {
943            (
944                String::new(),
945                format!("args.{}", f.name),
946                "    ",
947                String::new(),
948            )
949        } else {
950            (
951                format!(
952                    "    if let Some(v) = args.{} {{\n        wire::set_presence_bit(buf, base, {pos})?;",
953                    f.name
954                ),
955                "v".to_string(),
956                "        ",
957                "    }".to_string(),
958            )
959        };
960        if !open.is_empty() {
961            w!(out, "{open}");
962        }
963        match &f.ty {
964            Type::String => {
965                w!(
966                    out,
967                    "{indent}let off = wire::write_blob(buf, {val}.as_bytes())?;"
968                );
969                w!(out, "{indent}wire::patch_u32(buf, base + {slot}, off)?;");
970            }
971            Type::Bytes => {
972                w!(out, "{indent}let off = wire::write_blob(buf, {val})?;");
973                w!(out, "{indent}wire::patch_u32(buf, base + {slot}, off)?;");
974            }
975            Type::Struct(ci) => {
976                let cfn = snake(schema.type_name(*ci));
977                w!(out, "{indent}let off = write_{cfn}(buf, {val})?;");
978                w!(out, "{indent}wire::patch_u32(buf, base + {slot}, off)?;");
979            }
980            Type::List(elem) => {
981                emit_list_write(out, schema, elem, &val, slot, indent)?;
982            }
983            other => {
984                let info = scalar_info(other).expect("scalar");
985                w!(
986                    out,
987                    "{indent}wire::{}(buf, base + {slot}, {val})?;",
988                    info.put
989                );
990            }
991        }
992        if !close.is_empty() {
993            w!(out, "{close}");
994        }
995    }
996    w!(out, "    Ok(())");
997    w!(out, "}}");
998
999    // write fn (aligned block + fill).
1000    w!(out,);
1001    w!(
1002        out,
1003        "fn write_{fn_name}(buf: &mut Vec<u8>, args: &{args_ty}) -> Result<u32> {{"
1004    );
1005    w!(
1006        out,
1007        "    let base = wire::alloc_block(buf, {}, {})?;",
1008        lay.size,
1009        lay.align
1010    );
1011    w!(out, "    fill_{fn_name}_at(buf, base, args)?;");
1012    w!(out, "    Ok(base)");
1013    w!(out, "}}");
1014
1015    if is_root {
1016        w!(out,);
1017        w!(
1018            out,
1019            "/// Encode a message directly from typed args — single pass, no"
1020        );
1021        w!(out, "/// dynamic value tree.");
1022        w!(
1023            out,
1024            "pub fn encode_{fn_name}(args: &{args_ty}, mode: SchemaMode) -> Result<Vec<u8>> {{"
1025        );
1026        w!(out, "    let inline = matches!(mode, SchemaMode::Inline);");
1027        w!(out, "    let mut buf = wire::message_header(");
1028        w!(out, "        SCHEMA_ID,");
1029        w!(
1030            out,
1031            "        if inline {{ Some(SCHEMA_BYTES) }} else {{ None }},"
1032        );
1033        w!(
1034            out,
1035            "        {},",
1036            24 + schema.canonical_bytes().len() + lay.size as usize + 232
1037        );
1038        w!(out, "    )?;");
1039        w!(out, "    let root = write_{fn_name}(&mut buf, args)?;");
1040        w!(out, "    wire::finish_message(&mut buf, root);");
1041        w!(out, "    Ok(buf)");
1042        w!(out, "}}");
1043    }
1044    Ok(())
1045}
1046
1047fn emit_list_write(
1048    out: &mut String,
1049    schema: &Schema,
1050    elem: &Type,
1051    val: &str,
1052    slot: u32,
1053    indent: &str,
1054) -> Result<()> {
1055    let (stride, align) = elem_stride_align(schema, elem);
1056    w!(
1057        out,
1058        "{indent}let count = u32::try_from({val}.len()).map_err(|_| Error::MessageTooLarge)?;"
1059    );
1060    w!(
1061        out,
1062        "{indent}let loff = wire::begin_list(buf, count, {align})?;"
1063    );
1064    match elem {
1065        Type::Struct(ci) => {
1066            let sd = schema.struct_def_unchecked(*ci);
1067            // Bulk fast path for a list of a *dense, all-scalar* struct (fixed
1068            // stride, no presence bitmap, no heap children — e.g. a geometry
1069            // `Point`): reserve the whole region once and write every element's
1070            // fields directly into the &mut slice, so the per-element `fill_at`
1071            // call and its per-field bounds checks against the growing Vec go
1072            // away. Same bytes as the element-at-a-time path.
1073            let dense_scalar =
1074                sd.is_dense() && sd.fields.iter().all(|f| scalar_info(&f.ty).is_some());
1075            if dense_scalar {
1076                let elay = schema.layout_unchecked(*ci).as_fixed();
1077                w!(out, "{indent}let total = {val}.len().checked_mul({stride}).ok_or(Error::MessageTooLarge)?;");
1078                w!(out, "{indent}let ebase = wire::alloc_bytes(buf, total)?;");
1079                w!(out, "{indent}let region = &mut buf[ebase as usize..];");
1080                w!(out, "{indent}for (i, a) in {val}.iter().enumerate() {{");
1081                w!(out, "{indent}    let e = i * {stride};");
1082                for (pos, f) in sd.fields.iter().enumerate() {
1083                    let fslot = elay.slots[pos];
1084                    if matches!(f.ty, Type::Bool) {
1085                        w!(out, "{indent}    region[e + {fslot}] = a.{} as u8;", f.name);
1086                    } else {
1087                        let n = scalar_info(&f.ty).expect("scalar").size;
1088                        w!(out, "{indent}    region[e + {fslot}..e + {fslot} + {n}].copy_from_slice(&a.{}.to_le_bytes());", f.name);
1089                    }
1090                }
1091                w!(out, "{indent}}}");
1092            } else {
1093                let cfn = snake(schema.type_name(*ci));
1094                w!(out, "{indent}let total = {val}.len().checked_mul({stride}).ok_or(Error::MessageTooLarge)?;");
1095                w!(out, "{indent}let ebase = wire::alloc_bytes(buf, total)?;");
1096                w!(out, "{indent}for (i, a) in {val}.iter().enumerate() {{");
1097                w!(
1098                    out,
1099                    "{indent}    fill_{cfn}_at(buf, ebase + i as u32 * {stride}, a)?;"
1100                );
1101                w!(out, "{indent}}}");
1102            }
1103        }
1104        Type::String => {
1105            w!(
1106                out,
1107                "{indent}let total = {val}.len().checked_mul(4).ok_or(Error::MessageTooLarge)?;"
1108            );
1109            w!(out, "{indent}let slots = wire::alloc_bytes(buf, total)?;");
1110            w!(out, "{indent}for (i, s) in {val}.iter().enumerate() {{");
1111            w!(
1112                out,
1113                "{indent}    let off = wire::write_blob(buf, s.as_bytes())?;"
1114            );
1115            w!(
1116                out,
1117                "{indent}    wire::patch_u32(buf, slots + i as u32 * 4, off)?;"
1118            );
1119            w!(out, "{indent}}}");
1120        }
1121        other => {
1122            let info = scalar_info(other).expect("scalar");
1123            // Bulk slice write: reserve the element region once, then write it
1124            // in a single vectorizable pass (identical bytes to element-at-a-time).
1125            w!(out, "{indent}wire::{}(buf, {val});", info.push_slice);
1126        }
1127    }
1128    w!(out, "{indent}wire::patch_u32(buf, base + {slot}, loff)?;");
1129    Ok(())
1130}
1131
1132fn args_field_type(schema: &Schema, ty: &Type, dense: bool) -> String {
1133    let inner = match ty {
1134        Type::String => "&'a str".to_string(),
1135        Type::Bytes => "&'a [u8]".to_string(),
1136        Type::Struct(i) => {
1137            let cname = schema.type_name(*i);
1138            if args_needs_lifetime(schema, *i) {
1139                format!("&'a {cname}Args<'a>")
1140            } else {
1141                format!("&'a {cname}Args")
1142            }
1143        }
1144        Type::List(elem) => match elem.as_ref() {
1145            Type::Struct(i) => {
1146                let cname = schema.type_name(*i);
1147                if args_needs_lifetime(schema, *i) {
1148                    format!("&'a [{cname}Args<'a>]")
1149                } else {
1150                    format!("&'a [{cname}Args]")
1151                }
1152            }
1153            Type::String => "&'a [&'a str]".to_string(),
1154            other => format!("&'a [{}]", scalar_info(other).expect("scalar").rust),
1155        },
1156        other => scalar_info(other).expect("scalar").rust.to_string(),
1157    };
1158    if dense {
1159        inner
1160    } else {
1161        format!("Option<{inner}>")
1162    }
1163}
1164
1165// ---------------------------------------------------------------------------
1166// C++ emitter
1167// ---------------------------------------------------------------------------
1168
1169/// The C++ base type of a value (before optional-wrapping).
1170fn cpp_base_type(schema: &Schema, ty: &Type) -> String {
1171    match ty {
1172        Type::Bool => "bool".into(),
1173        Type::U8 => "uint8_t".into(),
1174        Type::U16 => "uint16_t".into(),
1175        Type::U32 => "uint32_t".into(),
1176        Type::U64 => "uint64_t".into(),
1177        Type::I8 => "int8_t".into(),
1178        Type::I16 => "int16_t".into(),
1179        Type::I32 => "int32_t".into(),
1180        Type::I64 => "int64_t".into(),
1181        Type::F32 => "float".into(),
1182        Type::F64 => "double".into(),
1183        Type::Enum(_) => "uint32_t".into(),
1184        Type::String => "std::string".into(),
1185        Type::Bytes => "std::vector<uint8_t>".into(),
1186        Type::Struct(ci) => schema.type_name(*ci).to_string(),
1187        Type::List(elem) => format!("std::vector<{}>", cpp_base_type(schema, elem)),
1188        Type::Map(_, _) | Type::Union(_) => {
1189            unreachable!("map/union types are rejected by reject_maps before emission")
1190        }
1191    }
1192}
1193
1194/// A C++ expression building a `veritate::Value` from `e` (a `cpp_base_type`
1195/// value). `depth` keeps nested-list lambda locals uniquely named.
1196fn cpp_pack_expr(ty: &Type, e: &str, depth: u32) -> String {
1197    match ty {
1198        Type::Bool => format!("veritate::Value::boolean({e})"),
1199        Type::U8 | Type::U16 | Type::U32 | Type::U64 => {
1200            format!("veritate::Value::u64(uint64_t({e}))")
1201        }
1202        Type::I8 | Type::I16 | Type::I32 | Type::I64 => {
1203            format!("veritate::Value::i64(int64_t({e}))")
1204        }
1205        Type::F32 | Type::F64 => format!("veritate::Value::f64(double({e}))"),
1206        Type::Enum(_) => format!("veritate::Value::en({e})"),
1207        Type::String => format!("veritate::Value::str({e})"),
1208        Type::Bytes => format!("veritate::Value::of_bytes({e})"),
1209        Type::Struct(_) => format!("({e})._pack()"),
1210        Type::List(elem) => {
1211            let inner = cpp_pack_expr(elem, &format!("__x{depth}"), depth + 1);
1212            format!(
1213                "[&]{{ std::vector<veritate::Value> __l{depth}; for (auto& __x{depth} : {e}) \
1214                 __l{depth}.push_back({inner}); return veritate::Value::of_list(std::move(__l{depth})); }}()"
1215            )
1216        }
1217        Type::Map(_, _) | Type::Union(_) => {
1218            unreachable!("map/union types are rejected by reject_maps before emission")
1219        }
1220    }
1221}
1222
1223/// A C++ expression building a `cpp_base_type` value from `r` (a `veritate::Ref`).
1224fn cpp_unpack_expr(schema: &Schema, ty: &Type, r: &str, depth: u32) -> String {
1225    match ty {
1226        Type::Bool => format!("{r}.b"),
1227        Type::U8 => format!("uint8_t({r}.u)"),
1228        Type::U16 => format!("uint16_t({r}.u)"),
1229        Type::U32 => format!("uint32_t({r}.u)"),
1230        Type::U64 => format!("uint64_t({r}.u)"),
1231        Type::I8 => format!("int8_t({r}.i)"),
1232        Type::I16 => format!("int16_t({r}.i)"),
1233        Type::I32 => format!("int32_t({r}.i)"),
1234        Type::I64 => format!("int64_t({r}.i)"),
1235        Type::F32 => format!("float({r}.d)"),
1236        Type::F64 => format!("double({r}.d)"),
1237        Type::Enum(_) => format!("{r}.e"),
1238        Type::String => format!("std::string({r}.str)"),
1239        Type::Bytes => {
1240            format!("std::vector<uint8_t>({r}.bytes_ptr, {r}.bytes_ptr + {r}.bytes_len)")
1241        }
1242        Type::Struct(ci) => format!("{}::_unpack(*{r}.st)", schema.type_name(*ci)),
1243        Type::List(elem) => {
1244            let base = cpp_base_type(schema, elem);
1245            let inner = cpp_unpack_expr(schema, elem, &format!("__e{depth}"), depth + 1);
1246            format!(
1247                "[&]{{ std::vector<{base}> __o{depth}; for (uint32_t __i{depth} = 0; \
1248                 __i{depth} < {r}.ls->size(); __i{depth}++) {{ auto __e{depth} = {r}.ls->get(__i{depth}); \
1249                 __o{depth}.push_back({inner}); }} return __o{depth}; }}()"
1250            )
1251        }
1252        Type::Map(_, _) | Type::Union(_) => {
1253            unreachable!("map/union types are rejected by reject_maps before emission")
1254        }
1255    }
1256}
1257
1258/// Struct type indices in dependency order: a struct that appears **by value**
1259/// as another's field (a nested `struct`, not `list<struct>` — a `vector`
1260/// tolerates an incomplete element) is emitted first, so `std::optional<T>`
1261/// members always see a complete `T`. Direct nesting is acyclic, so this
1262/// terminates.
1263fn cpp_struct_order(schema: &Schema) -> Vec<u16> {
1264    let mut order = Vec::new();
1265    let mut visited = BTreeSet::new();
1266    fn visit(schema: &Schema, idx: u16, visited: &mut BTreeSet<u16>, order: &mut Vec<u16>) {
1267        if !visited.insert(idx) {
1268            return;
1269        }
1270        if let Some(TypeDef::Struct(sd)) = schema.type_def(idx) {
1271            for f in &sd.fields {
1272                if let Type::Struct(ci) = &f.ty {
1273                    visit(schema, *ci, visited, order);
1274                }
1275            }
1276            order.push(idx);
1277        }
1278    }
1279    for idx in 0..schema.type_count() {
1280        if matches!(schema.type_def(idx), Some(TypeDef::Struct(_))) {
1281            visit(schema, idx, &mut visited, &mut order);
1282        }
1283    }
1284    order
1285}
1286
1287/// Compile a schema to a single self-contained C++ header of structs with
1288/// `to_verit` / `from_verit`, over the header-only `veritate.hpp` port. Like
1289/// the other emitters it maps each struct to/from the dynamic `Value`/reader
1290/// form and supports every schema shape (all struct modes, all list element
1291/// types). Fields are `std::optional<T>` (absent = `std::nullopt`). The
1292/// embedded canonical schema + 128-bit id are the cross-language contract.
1293pub fn generate_cpp(schema: &Schema) -> Result<String> {
1294    reject_maps(schema)?;
1295    for idx in 0..schema.type_count() {
1296        if let Some(TypeDef::Struct(sd)) = schema.type_def(idx) {
1297            check_ident(&sd.name)?;
1298            for f in &sd.fields {
1299                check_ident(&f.name)?;
1300            }
1301        }
1302    }
1303
1304    let mut out = String::new();
1305    w!(out, "// @generated by verit::codegen — do not edit.");
1306    w!(out, "// schema id: {:#034x}", schema.id());
1307    w!(out, "#pragma once");
1308    w!(out, "#include <cstdint>");
1309    w!(out, "#include <optional>");
1310    w!(out, "#include <string>");
1311    w!(out, "#include <vector>");
1312    w!(out, "#include \"veritate.hpp\"");
1313    w!(out,);
1314    w!(out, "namespace veritgen {{");
1315    w!(out,);
1316    let bytes: Vec<String> = schema
1317        .canonical_bytes()
1318        .iter()
1319        .map(|b| b.to_string())
1320        .collect();
1321    w!(
1322        out,
1323        "inline const char* SCHEMA_ID = \"{:032x}\";",
1324        schema.id()
1325    );
1326    w!(out, "inline const std::vector<uint8_t>& SCHEMA_BYTES() {{");
1327    w!(
1328        out,
1329        "    static const std::vector<uint8_t> b = {{{}}};",
1330        bytes.join(", ")
1331    );
1332    w!(out, "    return b;");
1333    w!(out, "}}");
1334    w!(out, "inline const veritate::Schema& _schema() {{");
1335    w!(
1336        out,
1337        "    static veritate::Schema s = veritate::Schema::from_canonical(SCHEMA_BYTES());"
1338    );
1339    w!(out, "    return s;");
1340    w!(out, "}}");
1341    w!(out, "inline const veritate::Resolver& _resolver() {{");
1342    w!(
1343        out,
1344        "    static veritate::Resolver r = veritate::Resolver::identity(_schema());"
1345    );
1346    w!(out, "    return r;");
1347    w!(out, "}}");
1348    w!(out,);
1349
1350    // Forward declarations so `list<struct>` members and out-of-line bodies
1351    // resolve regardless of definition order.
1352    for idx in 0..schema.type_count() {
1353        if let Some(TypeDef::Struct(sd)) = schema.type_def(idx) {
1354            w!(out, "struct {};", sd.name);
1355        }
1356    }
1357    w!(out,);
1358
1359    let order = cpp_struct_order(schema);
1360
1361    // Struct definitions (dependency order): members + method declarations.
1362    for &idx in &order {
1363        let sd = schema.struct_def_unchecked(idx);
1364        w!(out, "struct {} {{", sd.name);
1365        for f in &sd.fields {
1366            w!(
1367                out,
1368                "    std::optional<{}> {};",
1369                cpp_base_type(schema, &f.ty),
1370                f.name
1371            );
1372        }
1373        w!(out, "    veritate::Value _pack() const;");
1374        w!(
1375            out,
1376            "    static {} _unpack(const veritate::StructReader& __r);",
1377            sd.name
1378        );
1379        w!(
1380            out,
1381            "    std::vector<uint8_t> to_verit(veritate::SchemaMode mode = veritate::SchemaMode::Inline) const;"
1382        );
1383        w!(
1384            out,
1385            "    static {} from_verit(const std::vector<uint8_t>& __buf);",
1386            sd.name
1387        );
1388        w!(out, "}};");
1389        w!(out,);
1390    }
1391
1392    // Out-of-line bodies: every type is complete here.
1393    for &idx in &order {
1394        let sd = schema.struct_def_unchecked(idx);
1395        let name = &sd.name;
1396        w!(out, "inline veritate::Value {name}::_pack() const {{");
1397        w!(
1398            out,
1399            "    std::vector<std::pair<uint16_t, veritate::Value>> __f;"
1400        );
1401        for f in &sd.fields {
1402            let e = cpp_pack_expr(&f.ty, &format!("*{}", f.name), 0);
1403            w!(
1404                out,
1405                "    if ({}.has_value()) __f.push_back({{{}, {e}}});",
1406                f.name,
1407                f.id
1408            );
1409        }
1410        w!(out, "    return veritate::Value::strct(std::move(__f));");
1411        w!(out, "}}");
1412
1413        w!(
1414            out,
1415            "inline {name} {name}::_unpack(const veritate::StructReader& __r) {{"
1416        );
1417        w!(out, "    {name} __out;");
1418        for f in &sd.fields {
1419            // `get` yields std::optional<Ref>; read through `*__v`.
1420            let val = cpp_unpack_expr(schema, &f.ty, "(*__v)", 0);
1421            w!(
1422                out,
1423                "    {{ auto __v = __r.get({}); if (__v) __out.{} = {val}; }}",
1424                f.id,
1425                f.name
1426            );
1427        }
1428        w!(out, "    return __out;");
1429        w!(out, "}}");
1430
1431        w!(
1432            out,
1433            "inline std::vector<uint8_t> {name}::to_verit(veritate::SchemaMode mode) const {{"
1434        );
1435        w!(
1436            out,
1437            "    return veritate::encode(_schema(), _pack(), mode);"
1438        );
1439        w!(out, "}}");
1440
1441        w!(
1442            out,
1443            "inline {name} {name}::from_verit(const std::vector<uint8_t>& __buf) {{"
1444        );
1445        w!(
1446            out,
1447            "    veritate::Message __msg = veritate::Message::parse(__buf);"
1448        );
1449        w!(
1450            out,
1451            "    veritate::StructReader __root = __msg.root(_resolver());"
1452        );
1453        w!(out, "    return _unpack(__root);");
1454        w!(out, "}}");
1455        w!(out,);
1456    }
1457
1458    w!(out, "}}  // namespace veritgen");
1459    Ok(out)
1460}
1461
1462// ---------------------------------------------------------------------------
1463// Python emitter
1464// ---------------------------------------------------------------------------
1465
1466/// The runtime descriptor for a field type: how the shared pack/unpack helpers
1467/// in the generated module treat it. `"leaf"` = a native scalar/str/bytes/enum
1468/// (pass-through); `"struct"` = a nested generated dataclass; `"list"` = a list
1469/// of the inner descriptor.
1470fn py_desc(schema: &Schema, ty: &Type) -> String {
1471    match ty {
1472        Type::Struct(ci) => format!("(\"struct\", {})", schema.type_name(*ci)),
1473        Type::List(elem) => format!("(\"list\", {})", py_desc(schema, elem)),
1474        _ => "(\"leaf\", None)".to_string(),
1475    }
1476}
1477
1478/// A cosmetic Python type hint (annotations are strings under
1479/// `from __future__ import annotations`, so forward refs are fine).
1480fn py_hint(schema: &Schema, ty: &Type) -> String {
1481    match ty {
1482        Type::Bool => "bool".into(),
1483        Type::F32 | Type::F64 => "float".into(),
1484        Type::String => "str".into(),
1485        Type::Bytes => "bytes".into(),
1486        Type::Struct(ci) => schema.type_name(*ci).to_string(),
1487        Type::List(_) => "list".into(),
1488        _ => "int".into(), // integer scalars and open enums
1489    }
1490}
1491
1492/// Compile a schema to a Python module of `@dataclass`es with `to_verit` /
1493/// `from_verit`, over the pure-stdlib `veritate` port. Unlike the Rust emitter,
1494/// this supports every schema the dynamic reader does (all struct modes, all
1495/// list element types) — the dataclasses map to/from the `{field_id: value}`
1496/// form the port already speaks. The embedded canonical schema + 128-bit id are
1497/// the cross-language contract.
1498pub fn generate_python(schema: &Schema) -> Result<String> {
1499    reject_maps(schema)?;
1500    for idx in 0..schema.type_count() {
1501        if let Some(TypeDef::Struct(sd)) = schema.type_def(idx) {
1502            check_ident(&sd.name)?;
1503            for f in &sd.fields {
1504                check_ident(&f.name)?;
1505            }
1506        }
1507    }
1508
1509    let mut out = String::new();
1510    w!(out, "# @generated by verit::codegen — do not edit.");
1511    w!(out, "# schema id: {:#034x}", schema.id());
1512    w!(out, "from __future__ import annotations");
1513    w!(out, "from dataclasses import dataclass");
1514    w!(out, "import veritate as _v");
1515    w!(out,);
1516    let bytes: Vec<String> = schema
1517        .canonical_bytes()
1518        .iter()
1519        .map(|b| b.to_string())
1520        .collect();
1521    w!(out, "SCHEMA_ID = \"{:032x}\"", schema.id());
1522    w!(out, "SCHEMA_BYTES = bytes([{}])", bytes.join(", "));
1523    w!(out, "_SCHEMA = _v.Schema.from_canonical(SCHEMA_BYTES)");
1524    w!(out, "_RESOLVER = _v.Resolver.identity(_SCHEMA)");
1525    w!(out,);
1526
1527    // Shared runtime: map a dataclass <-> the {field_id: value} form.
1528    w!(out, "def _pack(obj):");
1529    w!(out, "    out = {{}}");
1530    w!(out, "    for (attr, fid, desc) in obj._VERIT:");
1531    w!(out, "        v = getattr(obj, attr)");
1532    w!(out, "        if v is not None:");
1533    w!(out, "            out[fid] = _pack_val(v, desc)");
1534    w!(out, "    return out");
1535    w!(out,);
1536    w!(out, "def _pack_val(v, desc):");
1537    w!(out, "    kind, extra = desc");
1538    w!(out, "    if kind == \"struct\":");
1539    w!(out, "        return _pack(v)");
1540    w!(out, "    if kind == \"list\":");
1541    w!(out, "        return [_pack_val(x, extra) for x in v]");
1542    w!(out, "    return v");
1543    w!(out,);
1544    w!(out, "def _unpack(cls, reader):");
1545    w!(out, "    kwargs = {{}}");
1546    w!(out, "    for (attr, fid, desc) in cls._VERIT:");
1547    w!(out, "        val = reader.get(fid)");
1548    w!(
1549        out,
1550        "        kwargs[attr] = None if val is None else _unpack_val(val, desc)"
1551    );
1552    w!(out, "    return cls(**kwargs)");
1553    w!(out,);
1554    w!(out, "def _unpack_val(val, desc):");
1555    w!(out, "    kind, extra = desc");
1556    w!(out, "    if kind == \"struct\":");
1557    w!(out, "        return _unpack(extra, val)");
1558    w!(out, "    if kind == \"list\":");
1559    w!(
1560        out,
1561        "        return [_unpack_val(val.get(i), extra) for i in range(len(val))]"
1562    );
1563    w!(out, "    return val");
1564    w!(out,);
1565
1566    // One dataclass per struct type.
1567    for idx in 0..schema.type_count() {
1568        let sd = match schema.type_def(idx) {
1569            Some(TypeDef::Struct(sd)) => sd,
1570            _ => continue,
1571        };
1572        w!(out, "@dataclass");
1573        w!(out, "class {}:", sd.name);
1574        if sd.fields.is_empty() {
1575            w!(out, "    pass");
1576        }
1577        for f in &sd.fields {
1578            w!(out, "    {}: {} = None", f.name, py_hint(schema, &f.ty));
1579        }
1580        w!(out, "    def to_verit(self, mode=_v.INLINE) -> bytes:");
1581        w!(out, "        return _v.encode(_SCHEMA, _pack(self), mode)");
1582        w!(out, "    @classmethod");
1583        w!(out, "    def from_verit(cls, buf) -> \"{}\":", sd.name);
1584        w!(out, "        msg = _v.Message.parse(buf)");
1585        w!(out, "        return _unpack(cls, msg.root(_RESOLVER))");
1586        w!(out,);
1587    }
1588
1589    // Field tables, set after all classes exist so struct refs resolve.
1590    for idx in 0..schema.type_count() {
1591        let sd = match schema.type_def(idx) {
1592            Some(TypeDef::Struct(sd)) => sd,
1593            _ => continue,
1594        };
1595        let entries: Vec<String> = sd
1596            .fields
1597            .iter()
1598            .map(|f| format!("(\"{}\", {}, {})", f.name, f.id, py_desc(schema, &f.ty)))
1599            .collect();
1600        w!(out, "{}._VERIT = [{}]", sd.name, entries.join(", "));
1601    }
1602    Ok(out)
1603}
1604
1605// ---------------------------------------------------------------------------
1606// TypeScript emitter
1607// ---------------------------------------------------------------------------
1608
1609/// Runtime descriptor for a field type, in the TS module's `_VERIT` tables:
1610/// `["leaf", null]` = a native scalar/str/bytes/enum; `["struct", Cls]` = a
1611/// nested generated class; `["list", inner]` = a list of the inner descriptor.
1612fn ts_desc(schema: &Schema, ty: &Type) -> String {
1613    match ty {
1614        Type::Struct(ci) => format!("[\"struct\", {}]", schema.type_name(*ci)),
1615        Type::List(elem) => format!("[\"list\", {}]", ts_desc(schema, elem)),
1616        _ => "[\"leaf\", null]".to_string(),
1617    }
1618}
1619
1620/// A cosmetic TS field type (the `| null` optionality is added by the caller).
1621fn ts_hint(schema: &Schema, ty: &Type) -> String {
1622    match ty {
1623        Type::Bool => "boolean".into(),
1624        Type::U64 | Type::I64 => "bigint".into(),
1625        Type::F32 | Type::F64 => "number".into(),
1626        Type::String => "string".into(),
1627        Type::Bytes => "Uint8Array".into(),
1628        Type::Struct(ci) => schema.type_name(*ci).to_string(),
1629        Type::List(elem) => format!("Array<{}>", ts_hint(schema, elem)),
1630        _ => "number".into(), // integer scalars <= 32 bits and open enums
1631    }
1632}
1633
1634/// Compile a schema to a TypeScript module of classes with `toVerit` /
1635/// `fromVerit` over the pure-`veritate.ts` port. Like the Python emitter it
1636/// supports every schema the dynamic reader does; classes map to/from the
1637/// port's `st([[id, value]])` form. The embedded canonical schema + 128-bit id
1638/// are the cross-language contract. Runs under Node's type stripping (the port
1639/// is imported as `./veritate.ts`), so no separate compile step is needed.
1640pub fn generate_ts(schema: &Schema) -> Result<String> {
1641    reject_maps(schema)?;
1642    for idx in 0..schema.type_count() {
1643        if let Some(TypeDef::Struct(sd)) = schema.type_def(idx) {
1644            check_ident(&sd.name)?;
1645            for f in &sd.fields {
1646                check_ident(&f.name)?;
1647            }
1648        }
1649    }
1650
1651    let mut out = String::new();
1652    w!(out, "// @generated by verit::codegen — do not edit.");
1653    w!(out, "// schema id: {:#034x}", schema.id());
1654    w!(out, "import * as _v from \"./veritate.ts\";");
1655    w!(out,);
1656    let bytes: Vec<String> = schema
1657        .canonical_bytes()
1658        .iter()
1659        .map(|b| b.to_string())
1660        .collect();
1661    w!(out, "export const SCHEMA_ID = \"{:032x}\";", schema.id());
1662    w!(
1663        out,
1664        "export const SCHEMA_BYTES = new Uint8Array([{}]);",
1665        bytes.join(", ")
1666    );
1667    w!(
1668        out,
1669        "const _SCHEMA = _v.Schema.fromCanonical(SCHEMA_BYTES);"
1670    );
1671    w!(out, "const _RESOLVER = _v.Resolver.identity(_SCHEMA);");
1672    w!(out,);
1673
1674    // Shared runtime: map a class instance <-> the port's st([[id, value]]) form.
1675    w!(
1676        out,
1677        "type _Desc = readonly [\"leaf\", null] | readonly [\"struct\", any] | readonly [\"list\", _Desc];"
1678    );
1679    w!(out,);
1680    w!(out, "function _pack(obj: any): _v.StructVal {{");
1681    w!(out, "  const out: Array<[number, _v.Value]> = [];");
1682    w!(
1683        out,
1684        "  for (const [attr, fid, desc] of obj.constructor._VERIT as Array<[string, number, _Desc]>) {{"
1685    );
1686    w!(out, "    const v = obj[attr];");
1687    w!(
1688        out,
1689        "    if (v !== null && v !== undefined) out.push([fid, _packVal(v, desc)]);"
1690    );
1691    w!(out, "  }}");
1692    w!(out, "  return _v.st(out);");
1693    w!(out, "}}");
1694    w!(out, "function _packVal(v: any, desc: _Desc): _v.Value {{");
1695    w!(out, "  if (desc[0] === \"struct\") return _pack(v);");
1696    w!(
1697        out,
1698        "  if (desc[0] === \"list\") return (v as any[]).map((x) => _packVal(x, desc[1]));"
1699    );
1700    w!(out, "  return v;");
1701    w!(out, "}}");
1702    w!(out, "function _unpack(cls: any, reader: any): any {{");
1703    w!(out, "  const obj = new cls();");
1704    w!(
1705        out,
1706        "  for (const [attr, fid, desc] of cls._VERIT as Array<[string, number, _Desc]>) {{"
1707    );
1708    w!(out, "    const val = reader.get(fid);");
1709    w!(
1710        out,
1711        "    obj[attr] = val === null || val === undefined ? null : _unpackVal(val, desc);"
1712    );
1713    w!(out, "  }}");
1714    w!(out, "  return obj;");
1715    w!(out, "}}");
1716    w!(out, "function _unpackVal(val: any, desc: _Desc): any {{");
1717    w!(
1718        out,
1719        "  if (desc[0] === \"struct\") return _unpack(desc[1], val);"
1720    );
1721    w!(out, "  if (desc[0] === \"list\") {{");
1722    w!(out, "    const out: any[] = [];");
1723    w!(
1724        out,
1725        "    for (let i = 0; i < val.length; i++) out.push(_unpackVal(val.get(i), desc[1]));"
1726    );
1727    w!(out, "    return out;");
1728    w!(out, "  }}");
1729    w!(out, "  return val;");
1730    w!(out, "}}");
1731    w!(out,);
1732
1733    // One class per struct type.
1734    for idx in 0..schema.type_count() {
1735        let sd = match schema.type_def(idx) {
1736            Some(TypeDef::Struct(sd)) => sd,
1737            _ => continue,
1738        };
1739        w!(out, "export class {} {{", sd.name);
1740        for f in &sd.fields {
1741            w!(
1742                out,
1743                "  {}: {} | null = null;",
1744                f.name,
1745                ts_hint(schema, &f.ty)
1746            );
1747        }
1748        w!(out, "  static _VERIT: Array<[string, number, _Desc]> = [];");
1749        w!(
1750            out,
1751            "  toVerit(mode: _v.SchemaMode = \"inline\"): Uint8Array {{ return _v.encode(_SCHEMA, _pack(this), mode); }}"
1752        );
1753        w!(
1754            out,
1755            "  static fromVerit(buf: Uint8Array): {} {{ return _unpack({}, _v.Message.parse(buf).root(_RESOLVER)); }}",
1756            sd.name,
1757            sd.name
1758        );
1759        w!(out, "}}");
1760    }
1761    w!(out,);
1762
1763    // Field tables, assigned after all classes exist so struct refs resolve.
1764    for idx in 0..schema.type_count() {
1765        let sd = match schema.type_def(idx) {
1766            Some(TypeDef::Struct(sd)) => sd,
1767            _ => continue,
1768        };
1769        let entries: Vec<String> = sd
1770            .fields
1771            .iter()
1772            .map(|f| format!("[\"{}\", {}, {}]", f.name, f.id, ts_desc(schema, &f.ty)))
1773            .collect();
1774        w!(out, "{}._VERIT = [{}];", sd.name, entries.join(", "));
1775    }
1776    Ok(out)
1777}
1778
1779// ---------------------------------------------------------------------------
1780// Go emitter
1781// ---------------------------------------------------------------------------
1782
1783/// Uppercase the first byte so a schema field name becomes an exported Go
1784/// field. (`check_ident` guarantees an ASCII-alphanumeric/underscore name.)
1785fn go_export(name: &str) -> String {
1786    let mut c = name.chars();
1787    match c.next() {
1788        Some(f) => f.to_ascii_uppercase().to_string() + c.as_str(),
1789        None => String::new(),
1790    }
1791}
1792
1793/// The Go type of a list element / nested value (struct elements are values,
1794/// not pointers — the reflect runtime dereferences as needed).
1795fn go_elem_type(schema: &Schema, ty: &Type) -> String {
1796    match ty {
1797        Type::Bool => "bool".into(),
1798        Type::U8 => "uint8".into(),
1799        Type::U16 => "uint16".into(),
1800        Type::U32 => "uint32".into(),
1801        Type::U64 => "uint64".into(),
1802        Type::I8 => "int8".into(),
1803        Type::I16 => "int16".into(),
1804        Type::I32 => "int32".into(),
1805        Type::I64 => "int64".into(),
1806        Type::F32 => "float32".into(),
1807        Type::F64 => "float64".into(),
1808        Type::Enum(_) => "uint32".into(),
1809        Type::String => "string".into(),
1810        Type::Bytes => "[]byte".into(),
1811        Type::Struct(ci) => schema.type_name(*ci).to_string(),
1812        Type::List(elem) => format!("[]{}", go_elem_type(schema, elem)),
1813        Type::Map(_, _) | Type::Union(_) => {
1814            unreachable!("map/union types are rejected by reject_maps before emission")
1815        }
1816    }
1817}
1818
1819/// The Go type of a top-level struct field. Optional fields are pointers (nil =
1820/// absent); `bytes`/lists are nilable slices; nested structs are pointers.
1821fn go_field_type(schema: &Schema, ty: &Type) -> String {
1822    match ty {
1823        Type::Bytes => "[]byte".into(),
1824        Type::List(elem) => format!("[]{}", go_elem_type(schema, elem)),
1825        Type::Struct(ci) => format!("*{}", schema.type_name(*ci)),
1826        other => format!("*{}", go_elem_type(schema, other)),
1827    }
1828}
1829
1830/// The runtime descriptor literal the reflect pack/unpack helpers walk.
1831fn go_desc(schema: &Schema, ty: &Type) -> String {
1832    match ty {
1833        Type::Struct(ci) => format!(
1834            "_desc{{kind: 1, typ: reflect.TypeOf({}{{}})}}",
1835            schema.type_name(*ci)
1836        ),
1837        Type::List(elem) => {
1838            format!("_desc{{kind: 2, sub: &{}}}", go_desc(schema, elem))
1839        }
1840        _ => "_desc{kind: 0}".to_string(),
1841    }
1842}
1843
1844/// Compile a schema to a Go package of typed structs with `ToVerit` /
1845/// `<Type>FromVerit` over the stdlib-only `veritate` port. A small reflect
1846/// runtime maps each struct to/from the port's `Struct` value form, so every
1847/// schema the dynamic reader supports is supported here too. Optional fields
1848/// are pointers (nil = absent). The embedded canonical schema + 128-bit id are
1849/// the cross-language contract.
1850pub fn generate_go(schema: &Schema) -> Result<String> {
1851    reject_maps(schema)?;
1852    for idx in 0..schema.type_count() {
1853        if let Some(TypeDef::Struct(sd)) = schema.type_def(idx) {
1854            check_ident(&sd.name)?;
1855            for f in &sd.fields {
1856                check_ident(&f.name)?;
1857            }
1858        }
1859    }
1860
1861    let mut out = String::new();
1862    w!(out, "// @generated by verit::codegen — do not edit.");
1863    w!(out, "// schema id: {:#034x}", schema.id());
1864    w!(out, "package veritgen");
1865    w!(out,);
1866    w!(out, "import (");
1867    w!(out, "\t\"reflect\"");
1868    w!(out,);
1869    w!(out, "\tv \"veritate\"");
1870    w!(out, ")");
1871    w!(out,);
1872    let bytes: Vec<String> = schema
1873        .canonical_bytes()
1874        .iter()
1875        .map(|b| b.to_string())
1876        .collect();
1877    w!(out, "const SchemaID = \"{:032x}\"", schema.id());
1878    w!(out,);
1879    w!(out, "var SchemaBytes = []byte{{{}}}", bytes.join(", "));
1880    w!(out,);
1881    w!(out, "var (");
1882    w!(out, "\t_schema   *v.Schema");
1883    w!(out, "\t_resolver *v.Resolver");
1884    w!(out, "\t_specs    = map[reflect.Type][]_fld{{}}");
1885    w!(out, ")");
1886    w!(out,);
1887    // The reflect runtime: descriptor + field spec + generic pack/unpack.
1888    w!(out, "// _desc.kind: 0 leaf, 1 struct, 2 list.");
1889    w!(out, "type _desc struct {{");
1890    w!(out, "\tkind int");
1891    w!(out, "\tsub  *_desc");
1892    w!(out, "\ttyp  reflect.Type");
1893    w!(out, "}}");
1894    w!(out, "type _fld struct {{");
1895    w!(out, "\tfield string");
1896    w!(out, "\tid    uint16");
1897    w!(out, "\td     _desc");
1898    w!(out, "}}");
1899    w!(out,);
1900    w!(out, "func _absent(fv reflect.Value) bool {{");
1901    w!(out, "\tswitch fv.Kind() {{");
1902    w!(
1903        out,
1904        "\tcase reflect.Ptr, reflect.Slice, reflect.Map, reflect.Interface:"
1905    );
1906    w!(out, "\t\treturn fv.IsNil()");
1907    w!(out, "\tdefault:");
1908    w!(out, "\t\treturn false");
1909    w!(out, "\t}}");
1910    w!(out, "}}");
1911    w!(
1912        out,
1913        "func _conv(x reflect.Value, t reflect.Type) reflect.Value {{"
1914    );
1915    w!(out, "\tif x.Type() == t {{");
1916    w!(out, "\t\treturn x");
1917    w!(out, "\t}}");
1918    w!(out, "\tif x.Type().ConvertibleTo(t) {{");
1919    w!(out, "\t\treturn x.Convert(t)");
1920    w!(out, "\t}}");
1921    w!(out, "\treturn x");
1922    w!(out, "}}");
1923    w!(out, "func _packObj(rv reflect.Value) v.Struct {{");
1924    w!(out, "\tif rv.Kind() == reflect.Ptr {{");
1925    w!(out, "\t\trv = rv.Elem()");
1926    w!(out, "\t}}");
1927    w!(out, "\tvar out []v.KV");
1928    w!(out, "\tfor _, f := range _specs[rv.Type()] {{");
1929    w!(out, "\t\tfv := rv.FieldByName(f.field)");
1930    w!(out, "\t\tif _absent(fv) {{");
1931    w!(out, "\t\t\tcontinue");
1932    w!(out, "\t\t}}");
1933    w!(
1934        out,
1935        "\t\tout = append(out, v.KV{{ID: f.id, V: _packVal(fv, f.d)}})"
1936    );
1937    w!(out, "\t}}");
1938    w!(out, "\treturn v.St(out...)");
1939    w!(out, "}}");
1940    w!(out, "func _packVal(fv reflect.Value, d _desc) any {{");
1941    w!(out, "\tif fv.Kind() == reflect.Ptr {{");
1942    w!(out, "\t\tfv = fv.Elem()");
1943    w!(out, "\t}}");
1944    w!(out, "\tswitch d.kind {{");
1945    w!(out, "\tcase 1:");
1946    w!(out, "\t\treturn _packObj(fv)");
1947    w!(out, "\tcase 2:");
1948    w!(out, "\t\tn := fv.Len()");
1949    w!(out, "\t\tarr := make([]any, n)");
1950    w!(out, "\t\tfor i := 0; i < n; i++ {{");
1951    w!(out, "\t\t\tarr[i] = _packVal(fv.Index(i), *d.sub)");
1952    w!(out, "\t\t}}");
1953    w!(out, "\t\treturn arr");
1954    w!(out, "\tdefault:");
1955    w!(out, "\t\treturn fv.Interface()");
1956    w!(out, "\t}}");
1957    w!(out, "}}");
1958    w!(
1959        out,
1960        "func _unpackObj(rt reflect.Type, r *v.StructReader) reflect.Value {{"
1961    );
1962    w!(out, "\tptr := reflect.New(rt)");
1963    w!(out, "\trv := ptr.Elem()");
1964    w!(out, "\tfor _, f := range _specs[rt] {{");
1965    w!(out, "\t\tval, ok := r.Get(f.id)");
1966    w!(out, "\t\tif !ok {{");
1967    w!(out, "\t\t\tcontinue");
1968    w!(out, "\t\t}}");
1969    w!(out, "\t\t_set(rv.FieldByName(f.field), f.d, val)");
1970    w!(out, "\t}}");
1971    w!(out, "\treturn ptr");
1972    w!(out, "}}");
1973    w!(out, "func _set(dst reflect.Value, d _desc, val any) {{");
1974    w!(out, "\tswitch d.kind {{");
1975    w!(out, "\tcase 1:");
1976    w!(out, "\t\tsr := val.(*v.StructReader)");
1977    w!(out, "\t\tet := dst.Type()");
1978    w!(out, "\t\tif et.Kind() == reflect.Ptr {{");
1979    w!(out, "\t\t\tet = et.Elem()");
1980    w!(out, "\t\t}}");
1981    w!(out, "\t\tobj := _unpackObj(et, sr)");
1982    w!(out, "\t\tif dst.Kind() == reflect.Ptr {{");
1983    w!(out, "\t\t\tdst.Set(obj)");
1984    w!(out, "\t\t}} else {{");
1985    w!(out, "\t\t\tdst.Set(obj.Elem())");
1986    w!(out, "\t\t}}");
1987    w!(out, "\tcase 2:");
1988    w!(out, "\t\tlr := val.(*v.ListReader)");
1989    w!(out, "\t\tn := lr.Len()");
1990    w!(out, "\t\tslice := reflect.MakeSlice(dst.Type(), n, n)");
1991    w!(out, "\t\tfor i := 0; i < n; i++ {{");
1992    w!(out, "\t\t\t_set(slice.Index(i), *d.sub, lr.Get(i))");
1993    w!(out, "\t\t}}");
1994    w!(out, "\t\tdst.Set(slice)");
1995    w!(out, "\tdefault:");
1996    w!(out, "\t\trv := reflect.ValueOf(val)");
1997    w!(out, "\t\tif dst.Kind() == reflect.Ptr {{");
1998    w!(out, "\t\t\tet := dst.Type().Elem()");
1999    w!(out, "\t\t\tp := reflect.New(et)");
2000    w!(out, "\t\t\tp.Elem().Set(_conv(rv, et))");
2001    w!(out, "\t\t\tdst.Set(p)");
2002    w!(out, "\t\t}} else {{");
2003    w!(out, "\t\t\tdst.Set(_conv(rv, dst.Type()))");
2004    w!(out, "\t\t}}");
2005    w!(out, "\t}}");
2006    w!(out, "}}");
2007    w!(out,);
2008
2009    // init: decode schema, build resolver, register field specs.
2010    w!(out, "func init() {{");
2011    w!(out, "\t_schema, _ = v.SchemaFromCanonical(SchemaBytes)");
2012    w!(out, "\t_resolver = v.Identity(_schema)");
2013    for idx in 0..schema.type_count() {
2014        let sd = match schema.type_def(idx) {
2015            Some(TypeDef::Struct(sd)) => sd,
2016            _ => continue,
2017        };
2018        w!(out, "\t_specs[reflect.TypeOf({}{{}})] = []_fld{{", sd.name);
2019        for f in &sd.fields {
2020            w!(
2021                out,
2022                "\t\t{{field: \"{}\", id: {}, d: {}}},",
2023                go_export(&f.name),
2024                f.id,
2025                go_desc(schema, &f.ty)
2026            );
2027        }
2028        w!(out, "\t}}");
2029    }
2030    w!(out, "}}");
2031    w!(out,);
2032
2033    // One struct type + ToVerit/FromVerit per schema struct.
2034    for idx in 0..schema.type_count() {
2035        let sd = match schema.type_def(idx) {
2036            Some(TypeDef::Struct(sd)) => sd,
2037            _ => continue,
2038        };
2039        w!(out, "type {} struct {{", sd.name);
2040        for f in &sd.fields {
2041            w!(
2042                out,
2043                "\t{} {}",
2044                go_export(&f.name),
2045                go_field_type(schema, &f.ty)
2046            );
2047        }
2048        w!(out, "}}");
2049        w!(out,);
2050        w!(
2051            out,
2052            "func (x *{}) ToVerit(mode v.SchemaMode) ([]byte, error) {{",
2053            sd.name
2054        );
2055        w!(
2056            out,
2057            "\treturn v.Encode(_schema, _packObj(reflect.ValueOf(x)), mode)"
2058        );
2059        w!(out, "}}");
2060        w!(
2061            out,
2062            "func {}FromVerit(buf []byte) (out *{}, err error) {{",
2063            sd.name,
2064            sd.name
2065        );
2066        w!(out, "\tdefer func() {{");
2067        w!(out, "\t\tif r := recover(); r != nil {{");
2068        w!(out, "\t\t\tif e, ok := r.(error); ok {{");
2069        w!(out, "\t\t\t\terr = e");
2070        w!(out, "\t\t\t}} else {{");
2071        w!(out, "\t\t\t\tpanic(r)");
2072        w!(out, "\t\t\t}}");
2073        w!(out, "\t\t}}");
2074        w!(out, "\t}}()");
2075        w!(out, "\tmsg, err := v.Parse(buf)");
2076        w!(out, "\tif err != nil {{");
2077        w!(out, "\t\treturn nil, err");
2078        w!(out, "\t}}");
2079        w!(out, "\troot, err := msg.Root(_resolver)");
2080        w!(out, "\tif err != nil {{");
2081        w!(out, "\t\treturn nil, err");
2082        w!(out, "\t}}");
2083        w!(
2084            out,
2085            "\treturn _unpackObj(reflect.TypeOf({}{{}}), root).Interface().(*{}), nil",
2086            sd.name,
2087            sd.name
2088        );
2089        w!(out, "}}");
2090        w!(out,);
2091    }
2092    Ok(out)
2093}