Skip to main content

capnpc/
codegen.rs

1// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
2// Licensed under the MIT License:
3//
4// Permission is hereby granted, free of charge, to any person obtaining a copy
5// of this software and associated documentation files (the "Software"), to deal
6// in the Software without restriction, including without limitation the rights
7// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8// copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20// THE SOFTWARE.
21
22use std::collections;
23use std::collections::{HashMap, HashSet};
24use std::path::{Path, PathBuf};
25
26use capnp::schema_capnp;
27use capnp::Error;
28
29use self::FormattedText::{BlankLine, Branch, Indent, Line};
30use crate::codegen_types::{do_branding, Leaf, RustNodeInfo, RustTypeInfo, TypeParameterTexts};
31use crate::convert_io_err;
32use crate::pointer_constants::generate_pointer_constant;
33
34/// An invocation of the capnpc-rust code generation plugin.
35pub struct CodeGenerationCommand {
36    output_directory: PathBuf,
37    default_parent_module: Vec<String>,
38    raw_code_generator_request_path: Option<PathBuf>,
39    capnp_root: String,
40    crates_provide_map: HashMap<u64, String>,
41}
42
43impl Default for CodeGenerationCommand {
44    fn default() -> Self {
45        Self {
46            output_directory: PathBuf::new(),
47            default_parent_module: Vec::new(),
48            raw_code_generator_request_path: None,
49            capnp_root: "::capnp".into(),
50            crates_provide_map: HashMap::new(),
51        }
52    }
53}
54
55impl CodeGenerationCommand {
56    /// Creates a new code generation command with default options.
57    pub fn new() -> Self {
58        Self::default()
59    }
60
61    /// Sets the output directory.
62    pub fn output_directory<P>(&mut self, path: P) -> &mut Self
63    where
64        P: AsRef<Path>,
65    {
66        self.output_directory = path.as_ref().to_path_buf();
67        self
68    }
69
70    /// Sets the default parent module, indicating the scope in your crate where you will
71    /// add the generated code.
72    ///
73    /// This option can be overridden by the `parentModule` annotation defined in `rust.capnp`.
74    pub fn default_parent_module(&mut self, default_parent_module: Vec<String>) -> &mut Self {
75        self.default_parent_module = default_parent_module;
76        self
77    }
78
79    /// Sets the root path for referencing things in the `capnp` crate from the generated
80    /// code. Usually this is `::capnp`. When we bootstrap schema.capnp we set this to `crate`.
81    /// If you are renaming the `capnp` crate when importing it, then you probably want to set
82    /// this value.
83    pub fn capnp_root(&mut self, capnp_root: &str) -> &mut Self {
84        self.capnp_root = capnp_root.into();
85        self
86    }
87
88    /// Sets the raw code generator request output path.
89    pub fn raw_code_generator_request_path<P>(&mut self, path: P) -> &mut Self
90    where
91        P: AsRef<Path>,
92    {
93        self.raw_code_generator_request_path = Some(path.as_ref().to_path_buf());
94        self
95    }
96
97    /// Sets the crate provides map.
98    ///
99    /// # Arguments
100    ///
101    /// - `map` - A map from capnp file id to the crate name that provides the
102    ///   corresponding generated code.
103    ///
104    /// See [`crate::CompilerCommand::crate_provides`] for more details.
105    pub fn crates_provide_map(&mut self, map: HashMap<u64, String>) -> &mut Self {
106        self.crates_provide_map = map;
107        self
108    }
109
110    /// Generates Rust code according to a `schema_capnp::code_generator_request` read from `inp`.
111    pub fn run<T>(&mut self, inp: T) -> ::capnp::Result<()>
112    where
113        T: std::io::Read,
114    {
115        use capnp::serialize;
116        use std::io::Write;
117
118        let message = serialize::read_message(inp, capnp::message::ReaderOptions::new())?;
119
120        let ctx = GeneratorContext::new_from_code_generation_command(self, &message)?;
121
122        for requested_file in ctx.request.get_requested_files()? {
123            let id = requested_file.get_id();
124            let mut filepath = self.output_directory.to_path_buf();
125            let requested = ::std::path::PathBuf::from(requested_file.get_filename()?.to_str()?);
126            filepath.push(requested);
127            if let Some(parent) = filepath.parent() {
128                ::std::fs::create_dir_all(parent).map_err(convert_io_err)?;
129            }
130
131            let root_name = path_to_stem_string(&filepath)?.replace('-', "_");
132            filepath.set_file_name(format!("{root_name}_capnp.rs"));
133
134            let capnp_version = match ctx.request.get_capnp_version() {
135                Ok(version) => format!(
136                    "{}.{}.{}",
137                    version.get_major(),
138                    version.get_minor(),
139                    version.get_micro()
140                ),
141                Err(_) => "<unknown>".to_owned(),
142            };
143
144            let lines =
145                Branch(vec![
146                Line(
147                    "// @generated by the capnpc-rust plugin to the Cap'n Proto schema compiler."
148                        .to_string(),
149                ),
150                line("// DO NOT EDIT."),
151                Line(format!("// source: {}", requested_file.get_filename()?.to_str()?)),
152                Line(format!("// capnp binary version: {}", capnp_version)),
153                Line(format!("// capnpc crate version: {}", env!("CARGO_PKG_VERSION"))),
154                BlankLine,
155                generate_node(&ctx, id, &root_name)?,
156            ]);
157
158            let text = stringify(&lines);
159
160            let previous_text = ::std::fs::read(&filepath);
161            if previous_text.is_ok() && previous_text.unwrap() == text.as_bytes() {
162                // File is unchanged. Do not write it so that builds with the
163                // output as part of the source work in read-only filesystems
164                // and so timestamp-based build systems and watchers do not get
165                // confused.
166                continue;
167            }
168
169            // It would be simpler to use the ? operator instead of a pattern match, but then the error message
170            // would not include `filepath`.
171            match ::std::fs::File::create(&filepath) {
172                Ok(mut writer) => {
173                    writer.write_all(text.as_bytes()).map_err(convert_io_err)?;
174                }
175                Err(e) => {
176                    let _ = writeln!(
177                        &mut ::std::io::stderr(),
178                        "could not open file {filepath:?} for writing: {e}"
179                    );
180                    return Err(convert_io_err(e));
181                }
182            }
183        }
184
185        if let Some(raw_code_generator_request) = &self.raw_code_generator_request_path {
186            let raw_code_generator_request_file =
187                ::std::fs::File::create(raw_code_generator_request).map_err(convert_io_err)?;
188            serialize::write_message_segments(
189                raw_code_generator_request_file,
190                &message.into_segments(),
191            )?;
192        }
193
194        Ok(())
195    }
196}
197
198pub struct GeneratorContext<'a> {
199    pub request: schema_capnp::code_generator_request::Reader<'a>,
200    pub node_map: collections::hash_map::HashMap<u64, schema_capnp::node::Reader<'a>>,
201    pub scope_map: collections::hash_map::HashMap<u64, Vec<String>>,
202
203    /// Map from node ID to the node ID of its parent scope. This is equal to node.scope_id
204    /// for all nodes except for autogenerated interface Param and Result structs;
205    /// those have scope_id set to 0. See the comment on paramStructType in schema.capnp.
206    pub node_parents: collections::hash_map::HashMap<u64, u64>,
207
208    /// Root path for referencing things in the `capnp` crate from the generated code.
209    pub capnp_root: String,
210}
211
212impl<'a> GeneratorContext<'a> {
213    pub fn new(
214        message: &'a capnp::message::Reader<capnp::serialize::OwnedSegments>,
215    ) -> ::capnp::Result<GeneratorContext<'a>> {
216        GeneratorContext::new_from_code_generation_command(&Default::default(), message)
217    }
218
219    fn new_from_code_generation_command(
220        code_generation_command: &CodeGenerationCommand,
221        message: &'a capnp::message::Reader<capnp::serialize::OwnedSegments>,
222    ) -> ::capnp::Result<GeneratorContext<'a>> {
223        let mut default_parent_module_scope = vec!["crate".to_string()];
224        default_parent_module_scope
225            .extend_from_slice(&code_generation_command.default_parent_module[..]);
226
227        let mut ctx = GeneratorContext {
228            request: message.get_root()?,
229            node_map: collections::hash_map::HashMap::<u64, schema_capnp::node::Reader<'a>>::new(),
230            scope_map: collections::hash_map::HashMap::<u64, Vec<String>>::new(),
231            node_parents: collections::hash_map::HashMap::new(),
232            capnp_root: code_generation_command.capnp_root.clone(),
233        };
234
235        let crates_provide = &code_generation_command.crates_provide_map;
236
237        for node in ctx.request.get_nodes()? {
238            ctx.node_map.insert(node.get_id(), node);
239            ctx.node_parents.insert(node.get_id(), node.get_scope_id());
240        }
241
242        // Fix up "anonymous" method params and results scopes.
243        for node in ctx.request.get_nodes()? {
244            if let Ok(schema_capnp::node::Interface(interface_reader)) = node.which() {
245                for method in interface_reader.get_methods()? {
246                    let param_struct_type = method.get_param_struct_type();
247                    if ctx.node_parents.get(&param_struct_type) == Some(&0) {
248                        ctx.node_parents.insert(param_struct_type, node.get_id());
249                    }
250                    let result_struct_type = method.get_result_struct_type();
251                    if ctx.node_parents.get(&result_struct_type) == Some(&0) {
252                        ctx.node_parents.insert(result_struct_type, node.get_id());
253                    }
254                }
255            }
256        }
257
258        for requested_file in ctx.request.get_requested_files()? {
259            let id = requested_file.get_id();
260
261            for import in requested_file.get_imports()? {
262                let importpath = ::std::path::Path::new(import.get_name()?.to_str()?);
263                let root_name: String = format!(
264                    "{}_capnp",
265                    path_to_stem_string(importpath)?.replace('-', "_")
266                );
267                let parent_module_scope = if let Some(krate) = crates_provide.get(&import.get_id())
268                {
269                    vec![format!("::{krate}")]
270                } else {
271                    default_parent_module_scope.clone()
272                };
273
274                ctx.populate_scope_map(
275                    parent_module_scope,
276                    root_name,
277                    NameKind::Verbatim,
278                    import.get_id(),
279                )?;
280            }
281
282            let root_name = path_to_stem_string(requested_file.get_filename()?.to_str()?)?;
283            let root_mod = format!("{}_capnp", root_name.replace('-', "_"));
284            ctx.populate_scope_map(
285                default_parent_module_scope.clone(),
286                root_mod,
287                NameKind::Verbatim,
288                id,
289            )?;
290        }
291        Ok(ctx)
292    }
293
294    fn get_last_name(&self, id: u64) -> ::capnp::Result<&str> {
295        match self.scope_map.get(&id) {
296            None => Err(Error::failed(format!("node not found: {id}"))),
297            Some(v) => match v.last() {
298                None => Err(Error::failed(format!("node has no scope: {id}"))),
299                Some(n) => Ok(n),
300            },
301        }
302    }
303
304    fn populate_scope_map(
305        &mut self,
306        mut ancestor_scope_names: Vec<String>,
307        mut current_node_name: String,
308        current_name_kind: NameKind,
309        node_id: u64,
310    ) -> ::capnp::Result<()> {
311        // unused nodes in imported files might be omitted from the node map
312        let Some(&node_reader) = self.node_map.get(&node_id) else {
313            return Ok(());
314        };
315
316        for annotation in node_reader.get_annotations()? {
317            if annotation.get_id() == NAME_ANNOTATION_ID {
318                current_node_name = name_annotation_value(annotation)?.to_string();
319            } else if annotation.get_id() == PARENT_MODULE_ANNOTATION_ID {
320                let head = ancestor_scope_names[0].clone();
321                ancestor_scope_names.clear();
322                ancestor_scope_names.push(head);
323                ancestor_scope_names.append(&mut get_parent_module(annotation)?);
324            }
325        }
326
327        let mut scope_names = ancestor_scope_names;
328        scope_names.push(capnp_name_to_rust_name(
329            &current_node_name,
330            current_name_kind,
331        ));
332
333        self.scope_map.insert(node_id, scope_names.clone());
334
335        let nested_nodes = node_reader.get_nested_nodes()?;
336        for nested_node in nested_nodes {
337            let nested_node_id = nested_node.get_id();
338            match self.node_map.get(&nested_node_id) {
339                None => {}
340                Some(node_reader) => match node_reader.which() {
341                    Ok(schema_capnp::node::Enum(_enum_reader)) => {
342                        self.populate_scope_map(
343                            scope_names.clone(),
344                            nested_node.get_name()?.to_string()?,
345                            NameKind::Verbatim,
346                            nested_node_id,
347                        )?;
348                    }
349                    _ => {
350                        self.populate_scope_map(
351                            scope_names.clone(),
352                            nested_node.get_name()?.to_string()?,
353                            NameKind::Module,
354                            nested_node_id,
355                        )?;
356                    }
357                },
358            }
359        }
360
361        if let Ok(schema_capnp::node::Struct(struct_reader)) = node_reader.which() {
362            let fields = struct_reader.get_fields()?;
363            for field in fields {
364                if let Ok(schema_capnp::field::Group(group)) = field.which() {
365                    self.populate_scope_map(
366                        scope_names.clone(),
367                        get_field_name(field)?.to_string(),
368                        NameKind::Module,
369                        group.get_type_id(),
370                    )?;
371                }
372            }
373        }
374
375        Ok(())
376    }
377
378    pub fn get_qualified_module(&self, type_id: u64) -> String {
379        self.scope_map[&type_id].join("::")
380    }
381}
382
383/// Like `format!(...)`, but adds a `capnp=ctx.capnp_root` argument.
384macro_rules! fmt(
385    ($ctx:ident, $($arg:tt)*) => ( format!($($arg)*, capnp=$ctx.capnp_root) )
386);
387
388pub(crate) use fmt;
389
390fn path_to_stem_string<P: AsRef<::std::path::Path>>(path: P) -> ::capnp::Result<String> {
391    if let Some(stem) = path.as_ref().file_stem() {
392        stem.to_owned()
393            .into_string()
394            .map_err(|os_string| Error::failed(format!("bad filename: {os_string:?}")))
395    } else {
396        Err(Error::failed(format!(
397            "file has no stem: {:?}",
398            path.as_ref()
399        )))
400    }
401}
402
403fn camel_to_snake_case(s: &str) -> String {
404    let mut result_chars: Vec<char> = Vec::new();
405    let mut first_char = true;
406    for c in s.chars() {
407        if c.is_uppercase() && !first_char {
408            result_chars.push('_');
409        }
410        result_chars.push(c.to_ascii_lowercase());
411        first_char = false;
412    }
413    result_chars.into_iter().collect()
414}
415
416fn capitalize_first_letter(s: &str) -> String {
417    let mut result_chars: Vec<char> = Vec::new();
418    for c in s.chars() {
419        result_chars.push(c)
420    }
421    result_chars[0] = result_chars[0].to_ascii_uppercase();
422    result_chars.into_iter().collect()
423}
424
425/// Formats a u64 into a string representation of the hex value, with
426/// separating underscores. Used instead of simple hex formatting to prevent
427/// clippy warnings in autogenerated code. This is loosely based off of
428/// similar functionality in the `separator` crate.
429fn format_u64(value: u64) -> String {
430    let hex = format!("{value:#x}");
431    let mut separated = hex[0..2].to_string();
432    let mut place = hex.len() - 2;
433    let mut later_loop = false;
434
435    for ch in hex[2..].chars() {
436        if later_loop && place % 4 == 0 {
437            separated.push('_');
438        }
439
440        separated.push(ch);
441        later_loop = true;
442        place -= 1;
443    }
444
445    separated
446}
447
448#[test]
449fn test_camel_to_snake_case() {
450    assert_eq!(camel_to_snake_case("fooBar"), "foo_bar".to_string());
451    assert_eq!(camel_to_snake_case("FooBar"), "foo_bar".to_string());
452    assert_eq!(camel_to_snake_case("fooBarBaz"), "foo_bar_baz".to_string());
453    assert_eq!(camel_to_snake_case("FooBarBaz"), "foo_bar_baz".to_string());
454    assert_eq!(camel_to_snake_case("helloWorld"), "hello_world".to_string());
455    assert_eq!(camel_to_snake_case("HelloWorld"), "hello_world".to_string());
456    assert_eq!(camel_to_snake_case("uint32Id"), "uint32_id".to_string());
457
458    assert_eq!(camel_to_snake_case("fooBar_"), "foo_bar_".to_string());
459}
460
461#[derive(PartialEq, Clone)]
462pub enum FormattedText {
463    Indent(Box<FormattedText>),
464    Branch(Vec<FormattedText>),
465    Line(String),
466    BlankLine,
467}
468
469impl From<Vec<FormattedText>> for FormattedText {
470    fn from(value: Vec<FormattedText>) -> Self {
471        Branch(value)
472    }
473}
474
475pub fn indent(inner: impl Into<FormattedText>) -> FormattedText {
476    Indent(Box::new(inner.into()))
477}
478
479pub fn line(inner: impl ToString) -> FormattedText {
480    Line(inner.to_string())
481}
482
483fn to_lines(ft: &FormattedText, indent: usize) -> Vec<String> {
484    match ft {
485        Indent(ft) => to_lines(ft, indent + 1),
486        Branch(fts) => fts.iter().flat_map(|ft| to_lines(ft, indent)).collect(),
487        Line(s) => {
488            let mut s1: String = " ".repeat(indent * 4);
489            s1.push_str(s);
490            vec![s1.to_string()]
491        }
492        BlankLine => vec!["".to_string()],
493    }
494}
495
496fn stringify(ft: &FormattedText) -> String {
497    let mut result = to_lines(ft, 0).join("\n");
498    result.push('\n');
499    result
500}
501
502const RUST_KEYWORDS: &[&str] = &[
503    "abstract", "alignof", "as", "be", "become", "box", "break", "const", "continue", "crate",
504    "do", "else", "enum", "extern", "false", "final", "fn", "for", "if", "impl", "in", "let",
505    "loop", "macro", "match", "mod", "move", "mut", "offsetof", "once", "override", "priv", "proc",
506    "pub", "pure", "ref", "return", "self", "sizeof", "static", "struct", "super", "trait", "true",
507    "type", "typeof", "unsafe", "unsized", "use", "virtual", "where", "while", "yield",
508];
509
510fn module_name(camel_case: &str) -> String {
511    let mut name = camel_to_snake_case(camel_case);
512    if RUST_KEYWORDS.contains(&&*name) {
513        name.push('_');
514    }
515    name
516}
517
518// Annotation IDs, as defined in rust.capnp.
519const NAME_ANNOTATION_ID: u64 = 0xc2fe4c6d100166d0;
520const PARENT_MODULE_ANNOTATION_ID: u64 = 0xabee386cd1450364;
521const OPTION_ANNOTATION_ID: u64 = 0xabfef22c4ee1964e;
522
523// StreamResult type ID, as defined in stream.capnp.
524const STREAM_RESULT_ID: u64 = 0x995f9a3377c0b16e;
525
526fn name_annotation_value(annotation: schema_capnp::annotation::Reader<'_>) -> capnp::Result<&str> {
527    if let schema_capnp::value::Text(t) = annotation.get_value()?.which()? {
528        let name = t?.to_str()?;
529        for c in name.chars() {
530            if !(c == '_' || c.is_alphanumeric()) {
531                return Err(capnp::Error::failed(
532                    "rust.name annotation value must only contain alphanumeric characters and '_'"
533                        .to_string(),
534                ));
535            }
536        }
537        Ok(name)
538    } else {
539        Err(capnp::Error::failed(
540            "expected rust.name annotation value to be of type Text".to_string(),
541        ))
542    }
543}
544
545fn get_field_name(field: schema_capnp::field::Reader<'_>) -> capnp::Result<&str> {
546    for annotation in field.get_annotations()? {
547        if annotation.get_id() == NAME_ANNOTATION_ID {
548            return name_annotation_value(annotation);
549        }
550    }
551    Ok(field.get_name()?.to_str()?)
552}
553
554fn get_enumerant_name(enumerant: schema_capnp::enumerant::Reader<'_>) -> capnp::Result<&str> {
555    for annotation in enumerant.get_annotations()? {
556        if annotation.get_id() == NAME_ANNOTATION_ID {
557            return name_annotation_value(annotation);
558        }
559    }
560    Ok(enumerant.get_name()?.to_str()?)
561}
562
563fn get_parent_module(annotation: schema_capnp::annotation::Reader) -> capnp::Result<Vec<String>> {
564    if let schema_capnp::value::Text(t) = annotation.get_value()?.which()? {
565        let module = t?.to_str()?;
566        Ok(module.split("::").map(|x| x.to_string()).collect())
567    } else {
568        Err(capnp::Error::failed(
569            "expected rust.parentModule annotation value to be of type Text".to_string(),
570        ))
571    }
572}
573#[derive(Clone, Copy)]
574enum NameKind {
575    // convert camel case to snake case, and avoid Rust keywords
576    Module,
577
578    // don't modify
579    Verbatim,
580}
581
582fn capnp_name_to_rust_name(capnp_name: &str, name_kind: NameKind) -> String {
583    match name_kind {
584        NameKind::Module => module_name(capnp_name),
585        NameKind::Verbatim => capnp_name.to_string(),
586    }
587}
588
589fn is_option_field(field: schema_capnp::field::Reader) -> capnp::Result<bool> {
590    use capnp::schema_capnp::*;
591
592    let enabled = field
593        .get_annotations()?
594        .iter()
595        .any(|a| a.get_id() == OPTION_ANNOTATION_ID);
596
597    if enabled {
598        let supported = match field.which()? {
599            field::Which::Group(_) => false,
600            field::Which::Slot(field) => {
601                let ty = field.get_type()?;
602                ty.is_pointer()? && !matches!(ty.which()?, type_::Interface(_))
603            }
604        };
605        if !supported {
606            return Err(capnp::Error::failed(
607                "$Rust.option annotation only supported on pointer fields (support for optional interfaces isn't implemented yet)".to_string(),
608            ));
609        }
610    }
611
612    Ok(enabled)
613}
614
615fn prim_default(value: &schema_capnp::value::Reader) -> ::capnp::Result<Option<String>> {
616    use capnp::schema_capnp::value;
617    match value.which()? {
618        value::Bool(false)
619        | value::Int8(0)
620        | value::Int16(0)
621        | value::Int32(0)
622        | value::Int64(0)
623        | value::Uint8(0)
624        | value::Uint16(0)
625        | value::Uint32(0)
626        | value::Uint64(0) => Ok(None),
627
628        value::Bool(true) => Ok(Some("true".to_string())),
629        value::Int8(i) => Ok(Some(i.to_string())),
630        value::Int16(i) => Ok(Some(i.to_string())),
631        value::Int32(i) => Ok(Some(i.to_string())),
632        value::Int64(i) => Ok(Some(i.to_string())),
633        value::Uint8(i) => Ok(Some(i.to_string())),
634        value::Uint16(i) => Ok(Some(i.to_string())),
635        value::Uint32(i) => Ok(Some(i.to_string())),
636        value::Uint64(i) => Ok(Some(i.to_string())),
637        value::Float32(f) => match f.classify() {
638            ::std::num::FpCategory::Zero => Ok(None),
639            _ => Ok(Some(format!("{}u32", f.to_bits()))),
640        },
641        value::Float64(f) => match f.classify() {
642            ::std::num::FpCategory::Zero => Ok(None),
643            _ => Ok(Some(format!("{}u64", f.to_bits()))),
644        },
645        _ => Err(Error::failed(
646            "Non-primitive value found where primitive was expected.".to_string(),
647        )),
648    }
649}
650
651// A primitive default *value* (not the mask) as a Rust expression, in the context of the field's
652// type. Used for the unmapped-leaf read in the erased carrier: an unmapped `@[...]` leaf reads its
653// default (matching C++'s `unmask<T>(0, mask)`), and the sentinel offset must not be dereferenced.
654fn prim_default_value(value: &schema_capnp::value::Reader) -> ::capnp::Result<String> {
655    use capnp::schema_capnp::value;
656    Ok(match value.which()? {
657        value::Bool(b) => b.to_string(),
658        value::Int8(i) => i.to_string(),
659        value::Int16(i) => i.to_string(),
660        value::Int32(i) => i.to_string(),
661        value::Int64(i) => i.to_string(),
662        value::Uint8(i) => i.to_string(),
663        value::Uint16(i) => i.to_string(),
664        value::Uint32(i) => i.to_string(),
665        value::Uint64(i) => i.to_string(),
666        value::Float32(f) => format!("f32::from_bits({}u32)", f.to_bits()),
667        value::Float64(f) => format!("f64::from_bits({}u64)", f.to_bits()),
668        _ => {
669            return Err(Error::failed(
670                "expected a primitive default value".to_string(),
671            ))
672        }
673    })
674}
675
676// True if a field is a scalar data field (read via `get_data_field`): a bool or fixed-width
677// integer/float. Such an accessor needs an explicit sentinel guard for an unmapped leaf, because
678// the out-of-range offset would overflow the bounds check on a 32-bit target. Pointer reads are
679// naturally safe (an out-of-range pointer index returns a null/default reader).
680fn is_scalar_data_field(field: &schema_capnp::field::Reader) -> ::capnp::Result<bool> {
681    use capnp::schema_capnp::*;
682    let field::Slot(s) = field.which()? else {
683        return Ok(false);
684    };
685    Ok(matches!(
686        s.get_type()?.which()?,
687        type_::Bool(())
688            | type_::Int8(())
689            | type_::Int16(())
690            | type_::Int32(())
691            | type_::Int64(())
692            | type_::Uint8(())
693            | type_::Uint16(())
694            | type_::Uint32(())
695            | type_::Uint64(())
696            | type_::Float32(())
697            | type_::Float64(())
698    ))
699}
700
701// Sentinel guard for an erased *builder* mutator writing at leaf `idx`: panic if the leaf is
702// unmapped (matching C++'s `KJ_REQUIRE`). The builder's raw writes are not bounds-checked, so
703// without this an unmapped `@[...]` set would be UB rather than a clean error.
704fn unmapped_panic(idx: usize, field_name: &str) -> String {
705    format!(
706        "if self.offsets[{idx}] == 0xffff_ffffu32 {{ panic!(\"field '{field_name}' is not mapped at this use site\"); }}"
707    )
708}
709
710// Gets the full list ordered of generic parameters for a node. Outer scopes come first.
711fn get_params(ctx: &GeneratorContext, mut node_id: u64) -> ::capnp::Result<Vec<String>> {
712    let mut result = Vec::new();
713
714    while node_id != 0 {
715        let node = ctx.node_map[&node_id];
716        let parameters = node.get_parameters()?;
717
718        for parameter in parameters.into_iter().rev() {
719            result.push(parameter.get_name()?.to_str()?.into());
720        }
721
722        node_id = node.get_scope_id();
723    }
724
725    result.reverse();
726    Ok(result)
727}
728
729//
730// Returns (type, getter body, default_decl)
731//
732pub fn getter_text(
733    ctx: &GeneratorContext,
734    field: &schema_capnp::field::Reader,
735    is_reader: bool,
736    is_fn: bool,
737) -> ::capnp::Result<(String, FormattedText, Option<FormattedText>)> {
738    getter_text_at(ctx, field, is_reader, is_fn, None)
739}
740
741// Like `getter_text`, but `offset_override`, when set, is used verbatim as the slot's offset
742// expression instead of the field's baked literal. The erased `AnyReader`/`AnyBuilder` pass
743// `self.offsets[i] as usize` so one type can serve every use site off a runtime offset table.
744fn getter_text_at(
745    ctx: &GeneratorContext,
746    field: &schema_capnp::field::Reader,
747    is_reader: bool,
748    is_fn: bool,
749    offset_override: Option<&str>,
750) -> ::capnp::Result<(String, FormattedText, Option<FormattedText>)> {
751    use capnp::schema_capnp::*;
752
753    match field.which()? {
754        field::Group(group) => {
755            let params = get_params(ctx, group.get_type_id())?;
756            let params_string = if params.is_empty() {
757                "".to_string()
758            } else {
759                format!(",{}", params.join(","))
760            };
761
762            let the_mod = ctx.get_qualified_module(group.get_type_id());
763
764            let mut result_type = if is_reader {
765                format!("{the_mod}::Reader<'a{params_string}>")
766            } else {
767                format!("{the_mod}::Builder<'a{params_string}>")
768            };
769
770            if is_fn {
771                result_type = format!("-> {result_type}");
772            }
773
774            let getter_code = if is_reader {
775                line("self.reader.into()")
776            } else {
777                line("self.builder.into()")
778            };
779
780            Ok((result_type, getter_code, None))
781        }
782        field::Slot(reg_field) => {
783            let mut default_decl = None;
784            let offset: String = match offset_override {
785                Some(e) => e.to_string(),
786                None => (reg_field.get_offset() as usize).to_string(),
787            };
788            let module_string = if is_reader { "Reader" } else { "Builder" };
789            let module = if is_reader {
790                Leaf::Reader("'a")
791            } else {
792                Leaf::Builder("'a")
793            };
794            let member = camel_to_snake_case(module_string);
795
796            fn primitive_case<T: PartialEq + ::std::fmt::Display>(
797                typ: &str,
798                member: &str,
799                offset: &str,
800                default: T,
801                zero: T,
802            ) -> String {
803                if default == zero {
804                    format!("self.{member}.get_data_field::<{typ}>({offset})")
805                } else {
806                    format!("self.{member}.get_data_field_mask::<{typ}>({offset}, {default})")
807                }
808            }
809
810            let raw_type = reg_field.get_type()?;
811            let inner_type = raw_type.type_string(ctx, module)?;
812            let default_value = reg_field.get_default_value()?;
813            let default = default_value.which()?;
814            let default_name = format!(
815                "DEFAULT_{}",
816                camel_to_snake_case(get_field_name(*field)?).to_ascii_uppercase()
817            );
818            let should_get_option = is_option_field(*field)?;
819
820            let typ = if should_get_option {
821                format!("Option<{inner_type}>")
822            } else {
823                inner_type
824            };
825
826            let (is_fallible, mut result_type) = match raw_type.which()? {
827                type_::Enum(_) => (
828                    true,
829                    fmt!(ctx, "::core::result::Result<{typ},{capnp}::NotInSchema>"),
830                ),
831                type_::AnyPointer(_) if !raw_type.is_parameter()? => (false, typ.clone()),
832                type_::Interface(_) => (
833                    true,
834                    fmt!(
835                        ctx,
836                        "{capnp}::Result<{}>",
837                        raw_type.type_string(ctx, Leaf::Client)?
838                    ),
839                ),
840                _ if raw_type.is_prim()? => (false, typ.clone()),
841                _ => (true, fmt!(ctx, "{capnp}::Result<{typ}>")),
842            };
843
844            if is_fn {
845                result_type = if result_type == "()" {
846                    "".to_string()
847                } else {
848                    format!("-> {result_type}")
849                }
850            }
851
852            let getter_fragment = match (raw_type.which()?, default) {
853                (type_::Void(()), value::Void(())) => {
854                    if is_fn {
855                        "".to_string()
856                    } else {
857                        "()".to_string()
858                    }
859                }
860                (type_::Bool(()), value::Bool(b)) => {
861                    if b {
862                        format!("self.{member}.get_bool_field_mask({offset}, true)")
863                    } else {
864                        format!("self.{member}.get_bool_field({offset})")
865                    }
866                }
867                (type_::Int8(()), value::Int8(i)) => primitive_case(&typ, &member, &offset, i, 0),
868                (type_::Int16(()), value::Int16(i)) => primitive_case(&typ, &member, &offset, i, 0),
869                (type_::Int32(()), value::Int32(i)) => primitive_case(&typ, &member, &offset, i, 0),
870                (type_::Int64(()), value::Int64(i)) => primitive_case(&typ, &member, &offset, i, 0),
871                (type_::Uint8(()), value::Uint8(i)) => primitive_case(&typ, &member, &offset, i, 0),
872                (type_::Uint16(()), value::Uint16(i)) => {
873                    primitive_case(&typ, &member, &offset, i, 0)
874                }
875                (type_::Uint32(()), value::Uint32(i)) => {
876                    primitive_case(&typ, &member, &offset, i, 0)
877                }
878                (type_::Uint64(()), value::Uint64(i)) => {
879                    primitive_case(&typ, &member, &offset, i, 0)
880                }
881                (type_::Float32(()), value::Float32(f)) => {
882                    primitive_case(&typ, &member, &offset, f.to_bits(), 0)
883                }
884                (type_::Float64(()), value::Float64(f)) => {
885                    primitive_case(&typ, &member, &offset, f.to_bits(), 0)
886                }
887                (type_::Enum(_), value::Enum(d)) => {
888                    if d == 0 {
889                        format!("::core::convert::TryFrom::try_from(self.{member}.get_data_field::<u16>({offset}))")
890                    } else {
891                        format!(
892                                "::core::convert::TryFrom::try_from(self.{member}.get_data_field_mask::<u16>({offset}, {d}))")
893                    }
894                }
895
896                (type_::Text(()), value::Text(_))
897                | (type_::Data(()), value::Data(_))
898                | (type_::List(_), value::List(_))
899                | (type_::Struct(_), value::Struct(_)) => {
900                    let default = if reg_field.get_had_explicit_default() {
901                        default_decl = Some(crate::pointer_constants::word_array_declaration(
902                            ctx,
903                            &default_name,
904                            ::capnp::raw::get_struct_pointer_section(default_value).get(0),
905                            crate::pointer_constants::WordArrayDeclarationOptions {
906                                pub_crate: true,
907                            },
908                        )?);
909                        format!("::core::option::Option::Some(&_private::{default_name}[..])")
910                    } else {
911                        "::core::option::Option::None".to_string()
912                    };
913
914                    if is_reader {
915                        fmt!(ctx,
916                            "{capnp}::traits::FromPointerReader::get_from_pointer(&self.{member}.get_pointer_field({offset}), {default})")
917                    } else {
918                        fmt!(ctx,"{capnp}::traits::FromPointerBuilder::get_from_pointer(self.{member}.get_pointer_field({offset}), {default})")
919                    }
920                }
921
922                (type_::Interface(_), value::Interface(_)) => {
923                    fmt!(ctx,"match self.{member}.get_pointer_field({offset}).get_capability() {{ ::core::result::Result::Ok(c) => ::core::result::Result::Ok({capnp}::capability::FromClientHook::new(c)), ::core::result::Result::Err(e) => ::core::result::Result::Err(e)}}")
924                }
925                (type_::AnyPointer(_), value::AnyPointer(_)) => {
926                    if !raw_type.is_parameter()? {
927                        fmt!(ctx,"{capnp}::any_pointer::{module_string}::new(self.{member}.get_pointer_field({offset}))")
928                    } else if is_reader {
929                        fmt!(ctx,"{capnp}::traits::FromPointerReader::get_from_pointer(&self.{member}.get_pointer_field({offset}), ::core::option::Option::None)")
930                    } else {
931                        fmt!(ctx,"{capnp}::traits::FromPointerBuilder::get_from_pointer(self.{member}.get_pointer_field({offset}), ::core::option::Option::None)")
932                    }
933                }
934                _ => return Err(Error::failed("default value was of wrong type".to_string())),
935            };
936
937            let getter_code = if should_get_option {
938                Branch(vec![
939                    Line(format!(
940                        "if self.{member}.is_pointer_field_null({offset}) {{"
941                    )),
942                    indent(Line(
943                        if is_fallible {
944                            "core::result::Result::Ok(core::option::Option::None)"
945                        } else {
946                            "::core::option::Option::None"
947                        }
948                        .to_string(),
949                    )),
950                    Line("} else {".to_string()),
951                    indent(Line(if is_fallible {
952                        format!("{getter_fragment}.map(::core::option::Option::Some)")
953                    } else {
954                        format!("::core::option::Option::Some({getter_fragment})")
955                    })),
956                    Line("}".to_string()),
957                ])
958            } else {
959                Line(getter_fragment)
960            };
961
962            Ok((result_type, getter_code, default_decl))
963        }
964    }
965}
966
967fn zero_fields_of_group(
968    ctx: &GeneratorContext,
969    node_id: u64,
970    clear: &mut bool,
971) -> ::capnp::Result<FormattedText> {
972    use capnp::schema_capnp::{field, node, type_};
973    match ctx.node_map[&node_id].which()? {
974        node::Struct(st) => {
975            let mut result = Vec::new();
976            if st.get_discriminant_count() != 0 {
977                result.push(Line(format!(
978                    "self.builder.set_data_field::<u16>({}, 0);",
979                    st.get_discriminant_offset()
980                )));
981            }
982            let fields = st.get_fields()?;
983            for field in fields {
984                match field.which()? {
985                    field::Group(group) => {
986                        result.push(zero_fields_of_group(ctx, group.get_type_id(), clear)?);
987                    }
988                    field::Slot(slot) => {
989                        let typ = slot.get_type()?.which()?;
990                        match typ {
991                            type_::Void(()) => {}
992                            type_::Bool(()) => {
993                                let line = Line(format!(
994                                    "self.builder.set_bool_field({}, false);",
995                                    slot.get_offset()
996                                ));
997                                // PERF could dedup more efficiently
998                                if !result.contains(&line) {
999                                    result.push(line)
1000                                }
1001                            }
1002                            type_::Int8(())
1003                            | type_::Int16(())
1004                            | type_::Int32(())
1005                            | type_::Int64(())
1006                            | type_::Uint8(())
1007                            | type_::Uint16(())
1008                            | type_::Uint32(())
1009                            | type_::Uint64(())
1010                            | type_::Float32(())
1011                            | type_::Float64(()) => {
1012                                let line = Line(format!(
1013                                    "self.builder.set_data_field::<{0}>({1}, 0{0});",
1014                                    slot.get_type()?.type_string(ctx, Leaf::Builder("'a"))?,
1015                                    slot.get_offset()
1016                                ));
1017                                // PERF could dedup more efficiently
1018                                if !result.contains(&line) {
1019                                    result.push(line)
1020                                }
1021                            }
1022                            type_::Enum(_) => {
1023                                let line = Line(format!(
1024                                    "self.builder.set_data_field::<u16>({}, 0u16);",
1025                                    slot.get_offset()
1026                                ));
1027                                // PERF could dedup more efficiently
1028                                if !result.contains(&line) {
1029                                    result.push(line)
1030                                }
1031                            }
1032                            type_::Struct(_)
1033                            | type_::List(_)
1034                            | type_::Text(())
1035                            | type_::Data(())
1036                            | type_::AnyPointer(_)
1037                            | type_::Interface(_) => {
1038                                // Is this the right thing to do for interfaces?
1039                                let line = Line(format!(
1040                                    "self.builder.reborrow().get_pointer_field({}).clear();",
1041                                    slot.get_offset()
1042                                ));
1043                                *clear = true;
1044                                // PERF could dedup more efficiently
1045                                if !result.contains(&line) {
1046                                    result.push(line)
1047                                }
1048                            }
1049                        }
1050                    }
1051                }
1052            }
1053            Ok(Branch(result))
1054        }
1055        _ => Err(Error::failed(
1056            "zero_fields_of_group() expected a struct".to_string(),
1057        )),
1058    }
1059}
1060
1061// One builder mutator (a `set_`, `init_`, or `initn_` method), split into its signature pieces
1062// and body so both the inherent `impl` and a newtype's Builder trait can be generated from a
1063// single source. `receiver`/`params`/`ret` compose the signature; `delegate_args` names the
1064// arguments a trait impl forwards when delegating to the inherent method.
1065struct SetterMethod {
1066    name: String,
1067    receiver: String,
1068    params: String,
1069    ret: String,
1070    body: FormattedText,
1071    delegate_args: String,
1072}
1073
1074impl SetterMethod {
1075    // The signature as it appears after `fn`, e.g. `set_foo(&mut self, value: i32)`. `bind_mut`
1076    // keeps a `mut self` receiver (the inherent body needs it); a delegating trait impl passes
1077    // false since it never mutates `self` locally.
1078    fn signature(&self, bind_mut: bool) -> String {
1079        let receiver = if bind_mut {
1080            self.receiver.clone()
1081        } else {
1082            self.receiver.trim_start_matches("mut ").to_string()
1083        };
1084        let args = if self.params.is_empty() {
1085            receiver
1086        } else {
1087            format!("{receiver}, {}", self.params)
1088        };
1089        let ret = if self.ret.is_empty() {
1090            String::new()
1091        } else {
1092            format!(" {}", self.ret)
1093        };
1094        format!("{}({args}){ret}", self.name)
1095    }
1096}
1097
1098fn setter_methods(
1099    ctx: &GeneratorContext,
1100    discriminant_offset: u32,
1101    styled_name: &str,
1102    field: &schema_capnp::field::Reader,
1103) -> ::capnp::Result<Vec<SetterMethod>> {
1104    setter_methods_at(
1105        ctx,
1106        &(discriminant_offset as usize).to_string(),
1107        styled_name,
1108        field,
1109        None,
1110    )
1111}
1112
1113// Like `setter_methods`, but with the discriminant offset and (via `offset_override`) the slot
1114// offset given as expressions, so the erased `AnyBuilder` can write at `self.offsets[i]` /
1115// `self.disc_offset` instead of baked literals.
1116fn setter_methods_at(
1117    ctx: &GeneratorContext,
1118    disc_offset: &str,
1119    styled_name: &str,
1120    field: &schema_capnp::field::Reader,
1121    offset_override: Option<&str>,
1122) -> ::capnp::Result<Vec<SetterMethod>> {
1123    use capnp::schema_capnp::*;
1124
1125    let mut setter_interior = Vec::new();
1126    let mut setter_param = "value".to_string();
1127    let mut initter_interior = Vec::new();
1128    let mut initter_mut = false;
1129    let mut initn_interior = Vec::new();
1130    let mut initter_params = Vec::new();
1131
1132    let discriminant_value = field.get_discriminant_value();
1133    if discriminant_value != field::NO_DISCRIMINANT {
1134        setter_interior.push(Line(format!(
1135            "self.builder.set_data_field::<u16>({}, {});",
1136            disc_offset, discriminant_value as usize
1137        )));
1138        let init_discrim = Line(format!(
1139            "self.builder.set_data_field::<u16>({}, {});",
1140            disc_offset, discriminant_value as usize
1141        ));
1142        initter_interior.push(init_discrim.clone());
1143        initn_interior.push(init_discrim);
1144    }
1145
1146    let mut return_result = false;
1147    let mut methods: Vec<SetterMethod> = Vec::new();
1148
1149    let (maybe_reader_type, maybe_builder_type): (Option<String>, Option<String>) = match field
1150        .which()?
1151    {
1152        field::Group(group) => {
1153            let params = get_params(ctx, group.get_type_id())?;
1154            let params_string = if params.is_empty() {
1155                "".to_string()
1156            } else {
1157                format!(",{}", params.join(","))
1158            };
1159
1160            let the_mod = ctx.get_qualified_module(group.get_type_id());
1161
1162            initter_interior.push(zero_fields_of_group(
1163                ctx,
1164                group.get_type_id(),
1165                &mut initter_mut,
1166            )?);
1167
1168            initter_interior.push(line("self.builder.into()"));
1169
1170            (None, Some(format!("{the_mod}::Builder<'a{params_string}>")))
1171        }
1172        field::Slot(reg_field) => {
1173            let offset: String = match offset_override {
1174                Some(e) => e.to_string(),
1175                None => (reg_field.get_offset() as usize).to_string(),
1176            };
1177            let typ = reg_field.get_type()?;
1178            match typ.which().expect("unrecognized type") {
1179                type_::Void(()) => {
1180                    setter_param = "_value".to_string();
1181                    (Some("()".to_string()), None)
1182                }
1183                type_::Bool(()) => {
1184                    match prim_default(&reg_field.get_default_value()?)? {
1185                        None => {
1186                            setter_interior.push(Line(format!(
1187                                "self.builder.set_bool_field({offset}, value);"
1188                            )));
1189                        }
1190                        Some(s) => {
1191                            setter_interior.push(Line(format!(
1192                                "self.builder.set_bool_field_mask({offset}, value, {s});"
1193                            )));
1194                        }
1195                    }
1196                    (Some("bool".to_string()), None)
1197                }
1198                _ if typ.is_prim()? => {
1199                    let tstr = typ.type_string(ctx, Leaf::Reader("'a"))?;
1200                    match prim_default(&reg_field.get_default_value()?)? {
1201                        None => {
1202                            setter_interior.push(Line(format!(
1203                                "self.builder.set_data_field::<{tstr}>({offset}, value);"
1204                            )));
1205                        }
1206                        Some(s) => {
1207                            setter_interior.push(Line(format!(
1208                                "self.builder.set_data_field_mask::<{tstr}>({offset}, value, {s});"
1209                            )));
1210                        }
1211                    };
1212                    (Some(tstr), None)
1213                }
1214                type_::Text(()) => {
1215                    // The text::Reader impl of SetterInput never fails, so we can unwrap().
1216                    setter_interior.push(Line(fmt!(ctx,
1217                        "{capnp}::traits::SetterInput::set_pointer_builder(self.builder.reborrow().get_pointer_field({offset}), value, false).unwrap()"
1218                    )));
1219                    initter_interior.push(Line(format!(
1220                        "self.builder.get_pointer_field({offset}).init_text(size)"
1221                    )));
1222                    initter_params.push("size: u32");
1223                    (
1224                        Some(fmt!(
1225                            ctx,
1226                            "impl {capnp}::traits::SetterInput<{capnp}::text::Owned>"
1227                        )),
1228                        Some(fmt!(ctx, "{capnp}::text::Builder<'a>")),
1229                    )
1230                }
1231                type_::Data(()) => {
1232                    setter_interior.push(Line(format!(
1233                        "self.builder.reborrow().get_pointer_field({offset}).set_data(value);"
1234                    )));
1235                    initter_interior.push(Line(format!(
1236                        "self.builder.get_pointer_field({offset}).init_data(size)"
1237                    )));
1238                    initter_params.push("size: u32");
1239                    (
1240                        Some(fmt!(ctx, "{capnp}::data::Reader<'_>")),
1241                        Some(fmt!(ctx, "{capnp}::data::Builder<'a>")),
1242                    )
1243                }
1244                type_::List(ls) => {
1245                    let et = ls.get_element_type()?;
1246                    return_result = true;
1247                    setter_interior.push(
1248                        Line(fmt!(ctx,"{capnp}::traits::SetterInput::set_pointer_builder(self.builder.reborrow().get_pointer_field({offset}), value, false)")));
1249
1250                    initter_params.push("size: u32");
1251                    initter_interior.push(
1252                        Line(fmt!(ctx,"{capnp}::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field({offset}), size)")));
1253
1254                    let mr = match et.which()? {
1255                        type_::Void(())
1256                        | type_::Bool(())
1257                        | type_::Int8(())
1258                        | type_::Int16(())
1259                        | type_::Int32(())
1260                        | type_::Int64(())
1261                        | type_::Uint8(())
1262                        | type_::Uint16(())
1263                        | type_::Uint32(())
1264                        | type_::Uint64(())
1265                        | type_::Float32(())
1266                        | type_::Float64(())
1267                        | type_::Enum(_)
1268                        | type_::Text(()) => {
1269                            // There are multiple SetterInput impls.
1270                            fmt!(
1271                                ctx,
1272                                "impl {capnp}::traits::SetterInput<{}>",
1273                                reg_field.get_type()?.type_string(ctx, Leaf::Owned)?
1274                            )
1275                        }
1276                        _ => reg_field.get_type()?.type_string(ctx, Leaf::Reader("'_"))?,
1277                    };
1278
1279                    (
1280                        Some(mr),
1281                        Some(
1282                            reg_field
1283                                .get_type()?
1284                                .type_string(ctx, Leaf::Builder("'a"))?,
1285                        ),
1286                    )
1287                }
1288                type_::Enum(e) => {
1289                    let id = e.get_type_id();
1290                    let the_mod = ctx.get_qualified_module(id);
1291                    if !reg_field.get_had_explicit_default() {
1292                        setter_interior.push(Line(format!(
1293                            "self.builder.set_data_field::<u16>({offset}, value as u16);"
1294                        )));
1295                    } else {
1296                        match reg_field.get_default_value()?.which()? {
1297                            schema_capnp::value::Enum(d) => {
1298                                setter_interior.push(Line(format!(
1299                                    "self.builder.set_data_field_mask::<u16>({offset}, value as u16, {d});"
1300                                )));
1301                            }
1302                            _ => return Err(Error::failed("enum default not an Enum".to_string())),
1303                        }
1304                    };
1305                    (Some(the_mod), None)
1306                }
1307                type_::Struct(_) => {
1308                    return_result = true;
1309                    initter_interior.push(
1310                      Line(fmt!(ctx,"{capnp}::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field({offset}), 0)")));
1311                    setter_interior.push(
1312                        Line(fmt!(ctx,"{capnp}::traits::SetterInput::set_pointer_builder(self.builder.reborrow().get_pointer_field({offset}), value, false)")));
1313
1314                    (
1315                        Some(typ.type_string(ctx, Leaf::Reader("'_"))?),
1316                        Some(typ.type_string(ctx, Leaf::Builder("'a"))?),
1317                    )
1318                }
1319                type_::Interface(_) => {
1320                    setter_interior.push(Line(format!(
1321                        "self.builder.reborrow().get_pointer_field({offset}).set_capability(value.client.hook);"
1322                    )));
1323                    (Some(typ.type_string(ctx, Leaf::Client)?), None)
1324                }
1325                type_::AnyPointer(_) => {
1326                    if typ.is_parameter()? {
1327                        initter_interior.push(Line(fmt!(ctx,"{capnp}::any_pointer::Builder::new(self.builder.get_pointer_field({offset})).init_as()")));
1328                        setter_interior.push(Line(fmt!(ctx,"{capnp}::traits::SetterInput::set_pointer_builder(self.builder.reborrow().get_pointer_field({offset}), value, false)")));
1329                        return_result = true;
1330
1331                        let builder_type = typ.type_string(ctx, Leaf::Builder("'a"))?;
1332
1333                        methods.push(SetterMethod {
1334                            name: format!("initn_{styled_name}"),
1335                            receiver: "self".into(),
1336                            params: "length: u32".into(),
1337                            ret: format!("-> {builder_type}"),
1338                            body: Branch(vec![
1339                                Branch(initn_interior),
1340                                Line(fmt!(ctx,"{capnp}::any_pointer::Builder::new(self.builder.get_pointer_field({offset})).initn_as(length)")),
1341                            ]),
1342                            delegate_args: "length".into(),
1343                        });
1344
1345                        (
1346                            Some(fmt!(
1347                                ctx,
1348                                "impl {capnp}::traits::SetterInput<{}>",
1349                                typ.type_string(ctx, Leaf::Owned)?
1350                            )),
1351                            Some(builder_type),
1352                        )
1353                    } else {
1354                        initter_interior.push(Line(fmt!(ctx,"let mut result = {capnp}::any_pointer::Builder::new(self.builder.get_pointer_field({offset}));")));
1355                        initter_interior.push(line("result.clear();"));
1356                        initter_interior.push(line("result"));
1357                        (None, Some(fmt!(ctx, "{capnp}::any_pointer::Builder<'a>")))
1358                    }
1359                }
1360                _ => return Err(Error::failed("unrecognized type".to_string())),
1361            }
1362        }
1363    };
1364    if let Some(reader_type) = maybe_reader_type {
1365        let ret = if return_result {
1366            fmt!(ctx, "-> {capnp}::Result<()>")
1367        } else {
1368            String::new()
1369        };
1370        methods.push(SetterMethod {
1371            name: format!("set_{styled_name}"),
1372            receiver: "&mut self".into(),
1373            params: format!("{setter_param}: {reader_type}"),
1374            ret,
1375            body: Branch(setter_interior),
1376            delegate_args: setter_param.clone(),
1377        });
1378    }
1379    if let Some(builder_type) = maybe_builder_type {
1380        let arg_names: Vec<&str> = initter_params
1381            .iter()
1382            .map(|p| p.split(':').next().unwrap_or("").trim())
1383            .collect();
1384        methods.push(SetterMethod {
1385            name: format!("init_{styled_name}"),
1386            receiver: if initter_mut { "mut self" } else { "self" }.into(),
1387            params: initter_params.join(", "),
1388            ret: format!("-> {builder_type}"),
1389            body: Branch(initter_interior),
1390            delegate_args: arg_names.join(", "),
1391        });
1392    }
1393    Ok(methods)
1394}
1395
1396// Assembles the inherent builder mutators for a field from `setter_methods`.
1397fn generate_setter(
1398    ctx: &GeneratorContext,
1399    discriminant_offset: u32,
1400    styled_name: &str,
1401    field: &schema_capnp::field::Reader,
1402) -> ::capnp::Result<FormattedText> {
1403    let mut result = Vec::new();
1404    for m in setter_methods(ctx, discriminant_offset, styled_name, field)? {
1405        result.push(line("#[inline]"));
1406        result.push(Line(format!("pub fn {} {{", m.signature(true))));
1407        result.push(indent(m.body));
1408        result.push(line("}"));
1409    }
1410    Ok(Branch(result))
1411}
1412
1413fn used_params_of_group(
1414    ctx: &GeneratorContext,
1415    group_id: u64,
1416    used_params: &mut HashSet<String>,
1417) -> capnp::Result<()> {
1418    let node = ctx.node_map[&group_id];
1419    match node.which()? {
1420        schema_capnp::node::Struct(st) => {
1421            for field in st.get_fields()? {
1422                match field.which()? {
1423                    schema_capnp::field::Group(group) => {
1424                        used_params_of_group(ctx, group.get_type_id(), used_params)?;
1425                    }
1426                    schema_capnp::field::Slot(slot) => {
1427                        used_params_of_type(ctx, slot.get_type()?, used_params)?;
1428                    }
1429                }
1430            }
1431            Ok(())
1432        }
1433        _ => Err(Error::failed("not a group".to_string())),
1434    }
1435}
1436
1437fn used_params_of_type(
1438    ctx: &GeneratorContext,
1439    ty: schema_capnp::type_::Reader,
1440    used_params: &mut HashSet<String>,
1441) -> capnp::Result<()> {
1442    use capnp::schema_capnp::type_;
1443    match ty.which()? {
1444        type_::List(ls) => {
1445            let et = ls.get_element_type()?;
1446            used_params_of_type(ctx, et, used_params)?;
1447        }
1448        type_::Enum(e) => {
1449            let node_id = e.get_type_id();
1450            let brand = e.get_brand()?;
1451            used_params_of_brand(ctx, node_id, brand, used_params)?;
1452        }
1453        type_::Struct(s) => {
1454            let node_id = s.get_type_id();
1455            let brand = s.get_brand()?;
1456            used_params_of_brand(ctx, node_id, brand, used_params)?;
1457        }
1458        type_::Interface(i) => {
1459            let node_id = i.get_type_id();
1460            let brand = i.get_brand()?;
1461            used_params_of_brand(ctx, node_id, brand, used_params)?;
1462        }
1463
1464        type_::AnyPointer(ap) => {
1465            if let type_::any_pointer::Parameter(def) = ap.which()? {
1466                let the_struct = &ctx.node_map[&def.get_scope_id()];
1467                let parameters = the_struct.get_parameters()?;
1468                let parameter = parameters.get(u32::from(def.get_parameter_index()));
1469                let parameter_name = parameter.get_name()?.to_str()?;
1470                used_params.insert(parameter_name.to_string());
1471            }
1472        }
1473        _ => (),
1474    }
1475    Ok(())
1476}
1477
1478fn used_params_of_brand(
1479    ctx: &GeneratorContext,
1480    node_id: u64,
1481    brand: schema_capnp::brand::Reader,
1482    used_params: &mut HashSet<String>,
1483) -> capnp::Result<()> {
1484    use schema_capnp::brand;
1485    let scopes = brand.get_scopes()?;
1486    let mut brand_scopes = HashMap::new();
1487    for scope in scopes {
1488        brand_scopes.insert(scope.get_scope_id(), scope);
1489    }
1490    let brand_scopes = brand_scopes; // freeze
1491    let mut current_node_id = node_id;
1492    while let Some(current_node) = ctx.node_map.get(&current_node_id) {
1493        let params = current_node.get_parameters()?;
1494        match brand_scopes.get(&current_node_id) {
1495            None => (),
1496            Some(scope) => match scope.which()? {
1497                brand::scope::Inherit(()) => {
1498                    for param in params {
1499                        used_params.insert(param.get_name()?.to_string()?);
1500                    }
1501                }
1502                brand::scope::Bind(bindings_list_opt) => {
1503                    let bindings_list = bindings_list_opt?;
1504                    assert_eq!(bindings_list.len(), params.len());
1505                    for binding in bindings_list {
1506                        match binding.which()? {
1507                            brand::binding::Unbound(()) => (),
1508                            brand::binding::Type(t) => {
1509                                used_params_of_type(ctx, t?, used_params)?;
1510                            }
1511                        }
1512                    }
1513                }
1514            },
1515        }
1516        current_node_id = current_node.get_scope_id();
1517    }
1518    Ok(())
1519}
1520
1521// return (the 'Which' enum, the 'which()' accessor, typedef, default_decls)
1522fn generate_union(
1523    ctx: &GeneratorContext,
1524    discriminant_offset: u32,
1525    fields: &[schema_capnp::field::Reader],
1526    is_reader: bool,
1527    params: &TypeParameterTexts,
1528) -> ::capnp::Result<(
1529    FormattedText,
1530    FormattedText,
1531    FormattedText,
1532    Vec<FormattedText>,
1533)> {
1534    use capnp::schema_capnp::*;
1535
1536    fn new_ty_param(ty_params: &mut Vec<String>) -> String {
1537        let result = format!("A{}", ty_params.len());
1538        ty_params.push(result.clone());
1539        result
1540    }
1541
1542    let mut getter_interior = Vec::new();
1543    let mut interior = Vec::new();
1544    let mut enum_interior = Vec::new();
1545    let mut default_decls = Vec::new();
1546
1547    let mut ty_params = Vec::new();
1548    let mut ty_args = Vec::new();
1549
1550    let mut used_params: HashSet<String> = HashSet::new();
1551
1552    let doffset = discriminant_offset as usize;
1553
1554    for field in fields {
1555        let dvalue = field.get_discriminant_value() as usize;
1556
1557        let field_name = get_field_name(*field)?;
1558        let enumerant_name = capitalize_first_letter(field_name);
1559
1560        let (ty, get, maybe_default_decl) = getter_text(ctx, field, is_reader, false)?;
1561        if let Some(default_decl) = maybe_default_decl {
1562            default_decls.push(default_decl)
1563        }
1564
1565        getter_interior.push(Branch(vec![
1566            Line(format!("{dvalue} => {{")),
1567            indent(Line(format!(
1568                "::core::result::Result::Ok({}(",
1569                enumerant_name.clone()
1570            ))),
1571            indent(indent(get)),
1572            indent(line("))")),
1573            line("}"),
1574        ]));
1575
1576        let ty1 = match field.which() {
1577            Ok(field::Group(group)) => {
1578                used_params_of_group(ctx, group.get_type_id(), &mut used_params)?;
1579                ty_args.push(ty);
1580                new_ty_param(&mut ty_params)
1581            }
1582            Ok(field::Slot(reg_field)) => {
1583                let fty = reg_field.get_type()?;
1584                used_params_of_type(ctx, fty, &mut used_params)?;
1585                match fty.which() {
1586                    Ok(
1587                        type_::Text(())
1588                        | type_::Data(())
1589                        | type_::List(_)
1590                        | type_::Struct(_)
1591                        | type_::AnyPointer(_),
1592                    ) => {
1593                        ty_args.push(ty);
1594                        new_ty_param(&mut ty_params)
1595                    }
1596                    Ok(type_::Interface(_)) => ty,
1597                    _ => ty,
1598                }
1599            }
1600            _ => ty,
1601        };
1602
1603        enum_interior.push(Line(format!("{enumerant_name}({ty1}),")));
1604    }
1605
1606    let enum_name = format!(
1607        "Which{}",
1608        if !ty_params.is_empty() {
1609            format!("<{}>", ty_params.join(","))
1610        } else {
1611            "".to_string()
1612        }
1613    );
1614
1615    getter_interior.push(Line(fmt!(
1616        ctx,
1617        "x => ::core::result::Result::Err({capnp}::NotInSchema(x))"
1618    )));
1619
1620    interior.push(Branch(vec![
1621        Line(format!("pub enum {enum_name} {{")),
1622        indent(enum_interior),
1623        line("}"),
1624    ]));
1625
1626    let result = Branch(interior);
1627
1628    let field_name = if is_reader { "reader" } else { "builder" };
1629
1630    let concrete_type = format!(
1631        "Which{}{}",
1632        if is_reader { "Reader" } else { "Builder" },
1633        if !ty_params.is_empty() {
1634            format!(
1635                "<'a,{}>",
1636                params
1637                    .expanded_list
1638                    .iter()
1639                    .filter(|s: &&String| used_params.contains(*s))
1640                    .cloned()
1641                    .collect::<Vec<String>>()
1642                    .join(",")
1643            )
1644        } else {
1645            "".to_string()
1646        }
1647    );
1648
1649    let typedef = Line(format!(
1650        "pub type {concrete_type} = Which{};",
1651        if !ty_args.is_empty() {
1652            format!("<{}>", ty_args.join(","))
1653        } else {
1654            "".to_string()
1655        }
1656    ));
1657
1658    let getter_result = Branch(vec![
1659        line("#[inline]"),
1660        Line(fmt!(ctx,
1661            "pub fn which(self) -> ::core::result::Result<{concrete_type}, {capnp}::NotInSchema> {{"
1662        )),
1663        indent(vec![
1664            Line(format!(
1665                "match self.{field_name}.get_data_field::<u16>({doffset}) {{"
1666            )),
1667            indent(getter_interior),
1668            line("}"),
1669        ]),
1670        line("}"),
1671    ]);
1672
1673    // TODO set_which() for builders?
1674
1675    Ok((result, getter_result, typedef, default_decls))
1676}
1677
1678fn generate_haser(
1679    discriminant_offset: u32,
1680    styled_name: &str,
1681    field: &schema_capnp::field::Reader,
1682    is_reader: bool,
1683) -> ::capnp::Result<FormattedText> {
1684    use capnp::schema_capnp::*;
1685
1686    let mut result = Vec::new();
1687    let mut interior = Vec::new();
1688    let member = if is_reader { "reader" } else { "builder" };
1689
1690    let discriminant_value = field.get_discriminant_value();
1691    if discriminant_value != field::NO_DISCRIMINANT {
1692        interior.push(Line(format!(
1693            "if self.{}.get_data_field::<u16>({}) != {} {{ return false; }}",
1694            member, discriminant_offset as usize, discriminant_value as usize
1695        )));
1696    }
1697    match field.which() {
1698        Err(_) | Ok(field::Group(_)) => {}
1699        Ok(field::Slot(reg_field)) => match reg_field.get_type()?.which()? {
1700            type_::Text(())
1701            | type_::Data(())
1702            | type_::List(_)
1703            | type_::Struct(_)
1704            | type_::Interface(_)
1705            | type_::AnyPointer(_) => {
1706                if is_reader {
1707                    interior.push(Line(format!(
1708                        "!self.{member}.get_pointer_field({}).is_null()",
1709                        reg_field.get_offset()
1710                    )));
1711                } else {
1712                    interior.push(Line(format!(
1713                        "!self.{member}.is_pointer_field_null({})",
1714                        reg_field.get_offset()
1715                    )));
1716                }
1717                result.push(line("#[inline]"));
1718                result.push(Line(format!("pub fn has_{styled_name}(&self) -> bool {{")));
1719                result.push(indent(interior));
1720                result.push(line("}"));
1721            }
1722            _ => {}
1723        },
1724    }
1725
1726    Ok(Branch(result))
1727}
1728
1729fn generate_pipeline_getter(
1730    ctx: &GeneratorContext,
1731    field: schema_capnp::field::Reader,
1732) -> ::capnp::Result<FormattedText> {
1733    use capnp::schema_capnp::{field, type_};
1734
1735    let name = get_field_name(field)?;
1736
1737    match field.which()? {
1738        field::Group(group) => {
1739            let params = get_params(ctx, group.get_type_id())?;
1740            let params_string = if params.is_empty() {
1741                "".to_string()
1742            } else {
1743                format!("<{}>", params.join(","))
1744            };
1745
1746            let the_mod = ctx.get_qualified_module(group.get_type_id());
1747            Ok(Branch(vec![
1748                Line(format!(
1749                    "pub fn get_{}(&self) -> {}::Pipeline{} {{",
1750                    camel_to_snake_case(name),
1751                    the_mod,
1752                    params_string
1753                )),
1754                indent(Line(fmt!(
1755                    ctx,
1756                    "{capnp}::capability::FromTypelessPipeline::new(self._typeless.noop())"
1757                ))),
1758                line("}"),
1759            ]))
1760        }
1761        field::Slot(reg_field) => {
1762            let typ = reg_field.get_type()?;
1763            match typ.which()? {
1764                type_::Struct(_) | type_::AnyPointer(_) => {
1765                    Ok(Branch(vec![
1766                        Line(format!("pub fn get_{}(&self) -> {} {{", camel_to_snake_case(name), typ.type_string(ctx, Leaf::Pipeline)?)),
1767                        indent(Line(fmt!(ctx,"{capnp}::capability::FromTypelessPipeline::new(self._typeless.get_pointer_field({}))", reg_field.get_offset()))),
1768                        line("}")
1769                    ]))
1770                }
1771                type_::Interface(_) => {
1772                    Ok(Branch(vec![
1773                        Line(format!("pub fn get_{}(&self) -> {} {{", camel_to_snake_case(name), typ.type_string(ctx, Leaf::Client)?)),
1774                        indent(Line(fmt!(ctx,"{capnp}::capability::FromClientHook::new(self._typeless.get_pointer_field({}).as_cap())", reg_field.get_offset()))),
1775                        line("}")
1776                    ]))
1777                }
1778                _ => {
1779                    Ok(Branch(Vec::new()))
1780                }
1781            }
1782        }
1783    }
1784}
1785
1786fn generate_get_field_types(
1787    ctx: &GeneratorContext,
1788    node_reader: schema_capnp::node::Reader,
1789) -> ::capnp::Result<FormattedText> {
1790    use capnp::schema_capnp::field;
1791    let st = match node_reader.which()? {
1792        schema_capnp::node::Struct(st) => st,
1793        _ => return Err(Error::failed("not a struct".into())),
1794    };
1795    let mut branches = vec![];
1796    for (index, field) in st.get_fields()?.iter().enumerate() {
1797        match field.which()? {
1798            field::Slot(slot) => {
1799                let raw_type = slot.get_type()?;
1800                let typ = raw_type.type_string(ctx, Leaf::Owned)?;
1801                branches.push(Line(fmt!(
1802                    ctx,
1803                    "{} => <{} as {capnp}::introspect::Introspect>::introspect(),",
1804                    index,
1805                    typ
1806                )));
1807            }
1808            field::Group(group) => {
1809                let params = get_params(ctx, group.get_type_id())?;
1810                let params_string = if params.is_empty() {
1811                    "".to_string()
1812                } else {
1813                    format!("<{}>", params.join(","))
1814                };
1815
1816                let the_mod = ctx.get_qualified_module(group.get_type_id());
1817
1818                let typ = format!("{the_mod}::Owned{params_string}");
1819                branches.push(Line(fmt!(
1820                    ctx,
1821                    "{} => <{} as {capnp}::introspect::Introspect>::introspect(),",
1822                    index,
1823                    typ
1824                )));
1825            }
1826        }
1827    }
1828    let body = if branches.is_empty() {
1829        Line(fmt!(
1830            ctx,
1831            "{capnp}::introspect::panic_invalid_field_index(index)"
1832        ))
1833    } else {
1834        branches.push(Line(fmt!(
1835            ctx,
1836            "_ => {capnp}::introspect::panic_invalid_field_index(index),"
1837        )));
1838        Branch(vec![
1839            Line("match index {".into()),
1840            indent(branches),
1841            Line("}".into()),
1842        ])
1843    };
1844    if !node_reader.get_is_generic() {
1845        Ok(Branch(vec![
1846            Line(fmt!(
1847                ctx,
1848                "pub(crate) fn get_field_types(index: u16) -> {capnp}::introspect::Type {{"
1849            )),
1850            indent(body),
1851            Line("}".into()),
1852        ]))
1853    } else {
1854        let params = node_reader.parameters_texts(ctx);
1855        Ok(Branch(vec![
1856            Line(fmt!(
1857                ctx,
1858                "pub(crate) fn get_field_types<{0}>(index: u16) -> {capnp}::introspect::Type {1} {{",
1859                params.params,
1860                params.where_clause
1861            )),
1862            indent(body),
1863            Line("}".into()),
1864        ]))
1865    }
1866}
1867
1868fn annotation_branch(
1869    ctx: &GeneratorContext,
1870    annotation: schema_capnp::annotation::Reader,
1871    child_index: Option<u16>,
1872    index: u32,
1873) -> ::capnp::Result<FormattedText> {
1874    use capnp::schema_capnp::node;
1875    let id = annotation.get_id();
1876    let annotation_decl = ctx.node_map[&id];
1877    let node::Annotation(a) = annotation_decl.which()? else {
1878        return Err(Error::failed("not an annotation node".into()));
1879    };
1880    if annotation_decl.get_is_generic() {
1881        let brand = annotation.get_brand()?;
1882        let the_mod = ctx.get_qualified_module(id);
1883        let func = do_branding(ctx, id, brand, Leaf::GetType, &the_mod)?;
1884        Ok(Line(format!("({child_index:?}, {index}) => {func}(),")))
1885    } else {
1886        // Avoid referring to the annotation in the generated code, so that users can import
1887        // annotation schemas like `c++.capnp` or `rust.capnp` without needing to generate code
1888        // for them, as long as the annotations are not generic.
1889        let ty = a.get_type()?;
1890        Ok(Line(fmt!(
1891            ctx,
1892            "({child_index:?}, {index}) => <{} as {capnp}::introspect::Introspect>::introspect(),",
1893            ty.type_string(ctx, Leaf::Owned)?
1894        )))
1895    }
1896}
1897
1898fn generate_get_annotation_types(
1899    ctx: &GeneratorContext,
1900    node_reader: schema_capnp::node::Reader,
1901) -> ::capnp::Result<FormattedText> {
1902    use capnp::schema_capnp::node;
1903
1904    let mut branches = vec![];
1905
1906    for (idx, annotation) in node_reader.get_annotations()?.iter().enumerate() {
1907        branches.push(annotation_branch(ctx, annotation, None, idx as u32)?);
1908    }
1909
1910    match node_reader.which()? {
1911        node::Struct(s) => {
1912            for (fidx, field) in s.get_fields()?.iter().enumerate() {
1913                for (idx, annotation) in field.get_annotations()?.iter().enumerate() {
1914                    branches.push(annotation_branch(
1915                        ctx,
1916                        annotation,
1917                        Some(fidx as u16),
1918                        idx as u32,
1919                    )?);
1920                }
1921            }
1922        }
1923        node::Enum(e) => {
1924            for (fidx, enumerant) in e.get_enumerants()?.iter().enumerate() {
1925                for (idx, annotation) in enumerant.get_annotations()?.iter().enumerate() {
1926                    branches.push(annotation_branch(
1927                        ctx,
1928                        annotation,
1929                        Some(fidx as u16),
1930                        idx as u32,
1931                    )?);
1932                }
1933            }
1934        }
1935        _ => (),
1936    }
1937
1938    let body = if branches.is_empty() {
1939        Line(fmt!(
1940            ctx,
1941            "{capnp}::introspect::panic_invalid_annotation_indices(child_index, index)"
1942        ))
1943    } else {
1944        branches.push(Line(fmt!(
1945            ctx,
1946            "_ => {capnp}::introspect::panic_invalid_annotation_indices(child_index, index),"
1947        )));
1948        indent(vec![
1949            Line("match (child_index, index) {".into()),
1950            indent(branches),
1951            Line("}".into()),
1952        ])
1953    };
1954
1955    if !node_reader.get_is_generic() {
1956        Ok(Branch(vec![
1957            Line(fmt!(ctx,"pub(crate) fn get_annotation_types(child_index: Option<u16>, index: u32) -> {capnp}::introspect::Type {{")),
1958            indent(body),
1959            Line("}".into()),
1960        ]))
1961    } else {
1962        let params = node_reader.parameters_texts(ctx);
1963        Ok(Branch(vec![
1964            Line(fmt!(ctx,
1965                "pub(crate) fn get_annotation_types<{0}>(child_index: Option<u16>, index: u32) -> {capnp}::introspect::Type {1} {{",
1966                params.params, params.where_clause
1967            )),
1968            indent(body),
1969            Line("}".into()),
1970        ]))
1971    }
1972}
1973
1974fn generate_members_by_discriminant(
1975    node_reader: schema_capnp::node::Reader,
1976) -> ::capnp::Result<FormattedText> {
1977    use capnp::schema_capnp::field;
1978    let st = match node_reader.which()? {
1979        schema_capnp::node::Struct(st) => st,
1980        _ => return Err(Error::failed("not a struct".into())),
1981    };
1982
1983    let mut union_member_indexes = vec![];
1984    let mut nonunion_member_indexes = vec![];
1985    for (index, field) in st.get_fields()?.iter().enumerate() {
1986        let disc = field.get_discriminant_value();
1987        if disc == field::NO_DISCRIMINANT {
1988            nonunion_member_indexes.push(index);
1989        } else {
1990            union_member_indexes.push((disc, index));
1991        }
1992    }
1993    union_member_indexes.sort();
1994
1995    let mut nonunion_string: String = "pub(crate) static NONUNION_MEMBERS : &[u16] = &[".into();
1996    for idx in 0..nonunion_member_indexes.len() {
1997        nonunion_string += &format!("{}", nonunion_member_indexes[idx]);
1998        if idx + 1 < nonunion_member_indexes.len() {
1999            nonunion_string += ",";
2000        }
2001    }
2002    nonunion_string += "];";
2003
2004    let mut members_by_disc: String =
2005        "pub(crate) static MEMBERS_BY_DISCRIMINANT : &[u16] = &[".into();
2006    for idx in 0..union_member_indexes.len() {
2007        let (disc, index) = union_member_indexes[idx];
2008        assert_eq!(idx, disc as usize);
2009        members_by_disc += &format!("{index}");
2010        if idx + 1 < union_member_indexes.len() {
2011            members_by_disc += ",";
2012        }
2013    }
2014    members_by_disc += "];";
2015    Ok(Branch(vec![Line(nonunion_string), Line(members_by_disc)]))
2016}
2017
2018fn generate_members_by_name(
2019    node_reader: schema_capnp::node::Reader,
2020) -> ::capnp::Result<FormattedText> {
2021    let st = match node_reader.which()? {
2022        schema_capnp::node::Struct(st) => st,
2023        _ => return Err(Error::failed("not a struct".into())),
2024    };
2025
2026    let mut members_by_name = Vec::new();
2027    for (index, field) in st.get_fields()?.iter().enumerate() {
2028        if let Ok(name) = get_field_name(field) {
2029            members_by_name.push((name, index));
2030        }
2031    }
2032    members_by_name.sort_by_key(|k| k.0);
2033
2034    let mut members_by_name_string: String =
2035        "pub(crate) static MEMBERS_BY_NAME : &[u16] = &[".into();
2036    for (i, (_, index)) in members_by_name.iter().enumerate() {
2037        members_by_name_string += &format!("{}", *index);
2038        if i + 1 < members_by_name.len() {
2039            members_by_name_string += ",";
2040        }
2041    }
2042    members_by_name_string += "];";
2043
2044    Ok(Branch(vec![Line(members_by_name_string)]))
2045}
2046
2047// The `type` newtypes a field was declared with, nearest first. A field declared `corner
2048// :ChainedPoint`, where `type ChainedPoint = Point` and `type Point = Vec3`, gives [ChainedPoint,
2049// Point, Vec3]. Empty for a field not declared with a newtype. A group field records its newtype
2050// in `Field.typeId`; a slot field in its type's `Type.typeId`. Each `type` node records the
2051// newtype it aliases (if any) in its own `Type.typeId`, which is how the chain is followed. A
2052// newtype whose node isn't in the request ends the chain there.
2053fn field_newtype_ids(
2054    ctx: &GeneratorContext,
2055    field: schema_capnp::field::Reader,
2056) -> ::capnp::Result<Vec<u64>> {
2057    use capnp::schema_capnp::*;
2058    let mut id = field.get_type_id();
2059    if id == 0 {
2060        if let field::Slot(slot) = field.which()? {
2061            id = slot.get_type()?.get_type_id();
2062        }
2063    }
2064    let mut ids = Vec::new();
2065    // The schema compiler rejects alias cycles; the `contains` check only stops a malformed
2066    // request from looping forever.
2067    while id != 0 && !ids.contains(&id) {
2068        ids.push(id);
2069        let Some(node) = ctx.node_map.get(&id) else {
2070            break;
2071        };
2072        let node::Type(Ok(t)) = node.which()? else {
2073            break;
2074        };
2075        id = t.get_type_id();
2076    }
2077    Ok(ids)
2078}
2079
2080// `FIELD_NEWTYPES`: for each field index, the newtype ids from `field_newtype_ids`. None (and
2081// nothing is emitted) when no field of the struct is declared with a newtype, which keeps
2082// generated code for schemas without newtypes unchanged.
2083fn generate_field_newtypes(
2084    ctx: &GeneratorContext,
2085    node_reader: schema_capnp::node::Reader,
2086) -> ::capnp::Result<Option<FormattedText>> {
2087    let schema_capnp::node::Struct(st) = node_reader.which()? else {
2088        return Err(Error::failed("not a struct".into()));
2089    };
2090    let mut any = false;
2091    let mut entries = Vec::new();
2092    for field in st.get_fields()? {
2093        let ids = field_newtype_ids(ctx, field)?;
2094        any |= !ids.is_empty();
2095        let ids: Vec<String> = ids.into_iter().map(format_u64).collect();
2096        entries.push(format!("&[{}]", ids.join(", ")));
2097    }
2098    if !any {
2099        return Ok(None);
2100    }
2101    Ok(Some(Line(format!(
2102        "pub(crate) static FIELD_NEWTYPES: &[&[u64]] = &[{}];",
2103        entries.join(", ")
2104    ))))
2105}
2106
2107// We need this to work around the fact that Rust does not allow typedefs
2108// with unused type parameters.
2109fn get_ty_params_of_brand(
2110    ctx: &GeneratorContext,
2111    brand: schema_capnp::brand::Reader,
2112) -> ::capnp::Result<String> {
2113    let mut acc = HashSet::new();
2114    get_ty_params_of_brand_helper(ctx, &mut acc, brand)?;
2115    let mut result = String::new();
2116    for (scope_id, parameter_index) in acc.into_iter() {
2117        let node = ctx.node_map[&scope_id];
2118        let p = node.get_parameters()?.get(u32::from(parameter_index));
2119        result.push_str(p.get_name()?.to_str()?);
2120        result.push(',');
2121    }
2122
2123    Ok(result)
2124}
2125
2126fn get_ty_params_of_type_helper(
2127    ctx: &GeneratorContext,
2128    accumulator: &mut HashSet<(u64, u16)>,
2129    typ: schema_capnp::type_::Reader,
2130) -> ::capnp::Result<()> {
2131    use capnp::schema_capnp::type_;
2132    match typ.which()? {
2133        type_::Void(())
2134        | type_::Bool(())
2135        | type_::Int8(())
2136        | type_::Int16(())
2137        | type_::Int32(())
2138        | type_::Int64(())
2139        | type_::Uint8(())
2140        | type_::Uint16(())
2141        | type_::Uint32(())
2142        | type_::Uint64(())
2143        | type_::Float32(())
2144        | type_::Float64(())
2145        | type_::Text(_)
2146        | type_::Data(_) => {}
2147        type_::AnyPointer(p) => {
2148            match p.which()? {
2149                type_::any_pointer::Unconstrained(_) => (),
2150                type_::any_pointer::Parameter(p) => {
2151                    accumulator.insert((p.get_scope_id(), p.get_parameter_index()));
2152                }
2153                type_::any_pointer::ImplicitMethodParameter(_) => {
2154                    // XXX
2155                }
2156            }
2157        }
2158        type_::List(list) => {
2159            get_ty_params_of_type_helper(ctx, accumulator, list.get_element_type()?)?
2160        }
2161        type_::Enum(e) => {
2162            get_ty_params_of_brand_helper(ctx, accumulator, e.get_brand()?)?;
2163        }
2164        type_::Struct(s) => {
2165            get_ty_params_of_brand_helper(ctx, accumulator, s.get_brand()?)?;
2166        }
2167        type_::Interface(interf) => {
2168            get_ty_params_of_brand_helper(ctx, accumulator, interf.get_brand()?)?;
2169        }
2170    }
2171    Ok(())
2172}
2173
2174fn get_ty_params_of_brand_helper(
2175    ctx: &GeneratorContext,
2176    accumulator: &mut HashSet<(u64, u16)>,
2177    brand: schema_capnp::brand::Reader,
2178) -> ::capnp::Result<()> {
2179    for scope in brand.get_scopes()? {
2180        let scope_id = scope.get_scope_id();
2181        match scope.which()? {
2182            schema_capnp::brand::scope::Bind(bind) => {
2183                for binding in bind? {
2184                    match binding.which()? {
2185                        schema_capnp::brand::binding::Unbound(()) => {}
2186                        schema_capnp::brand::binding::Type(t) => {
2187                            get_ty_params_of_type_helper(ctx, accumulator, t?)?
2188                        }
2189                    }
2190                }
2191            }
2192            schema_capnp::brand::scope::Inherit(()) => {
2193                let parameters = ctx.node_map[&scope_id].get_parameters()?;
2194                for idx in 0..parameters.len() {
2195                    accumulator.insert((scope_id, idx as u16));
2196                }
2197            }
2198        }
2199    }
2200    Ok(())
2201}
2202
2203// A `type` newtype declaration (`type Vec3 = group { ... }`) is represented as a `type` node
2204// whose target is a struct pointing at a template node the newtype owns (its `scope_id` is the
2205// `type` node). We give such a newtype a semantic trait -- `vec3::Reader` -- so every use site
2206// shares one name. Returns the template node's id when `alias_id` is an inline group/union
2207// newtype, or a `type` aliasing one (`type Bar = Foo`), which shares Foo's template.
2208fn newtype_template_id(ctx: &GeneratorContext, alias_id: u64) -> ::capnp::Result<Option<u64>> {
2209    use capnp::schema_capnp::*;
2210    let Some(alias) = ctx.node_map.get(&alias_id) else {
2211        return Ok(None);
2212    };
2213    let node::Type(Ok(t)) = alias.which()? else {
2214        return Ok(None);
2215    };
2216    let type_::Struct(st) = t.which()? else {
2217        return Ok(None); // scalar newtype -- rendered via its underlying type
2218    };
2219    let template_id = st.get_type_id();
2220    let Some(template) = ctx.node_map.get(&template_id) else {
2221        return Ok(None);
2222    };
2223    if template.get_scope_id() != alias_id {
2224        // Not this node's own template. Either it aliases another inline newtype (recorded as
2225        // Type.typeId), whose template it shares, or it names a pre-existing struct.
2226        let aliased = t.get_type_id();
2227        if aliased != 0 {
2228            return newtype_template_id(ctx, aliased);
2229        }
2230        return Ok(None);
2231    }
2232    Ok(Some(template_id))
2233}
2234
2235// The inline newtype owning `alias_id`'s template: `alias_id` itself, or for `type Bar = Foo`
2236// (Foo an inline group/union newtype), Foo. None if `alias_id` isn't an inline newtype.
2237fn inline_newtype_owner(ctx: &GeneratorContext, alias_id: u64) -> ::capnp::Result<Option<u64>> {
2238    Ok(newtype_template_id(ctx, alias_id)?.map(|t| ctx.node_map[&t].get_scope_id()))
2239}
2240
2241// A non-union group newtype. Its fields are slots and/or nested-newtype members (group fields
2242// carrying a `Field.typeId`, surfaced as associated types). A plain anonymous group field is not
2243// yet supported.
2244fn group_newtype_template_id(
2245    ctx: &GeneratorContext,
2246    alias_id: u64,
2247) -> ::capnp::Result<Option<u64>> {
2248    use capnp::schema_capnp::*;
2249    let Some(template_id) = newtype_template_id(ctx, alias_id)? else {
2250        return Ok(None);
2251    };
2252    let node::Struct(tmpl) = ctx.node_map[&template_id].which()? else {
2253        return Ok(None);
2254    };
2255    if tmpl.get_discriminant_count() > 0 {
2256        return Ok(None); // union newtype -- handled separately
2257    }
2258    for field in tmpl.get_fields()? {
2259        if let field::Group(_) = field.which()? {
2260            if field.get_type_id() == 0 {
2261                return Ok(None); // anonymous group member -- not a newtype, deferred
2262            }
2263        }
2264    }
2265    Ok(Some(template_id))
2266}
2267
2268// A union newtype. Slot arms have use-site-independent `Which` payloads; a group arm is a nested
2269// newtype, surfaced through an associated type. An anonymous group arm is not yet supported.
2270fn union_newtype_template_id(
2271    ctx: &GeneratorContext,
2272    alias_id: u64,
2273) -> ::capnp::Result<Option<u64>> {
2274    use capnp::schema_capnp::*;
2275    let Some(template_id) = newtype_template_id(ctx, alias_id)? else {
2276        return Ok(None);
2277    };
2278    let node::Struct(tmpl) = ctx.node_map[&template_id].which()? else {
2279        return Ok(None);
2280    };
2281    if tmpl.get_discriminant_count() == 0 {
2282        return Ok(None); // not a union
2283    }
2284    for field in tmpl.get_fields()? {
2285        if let field::Group(_) = field.which()? {
2286            if field.get_type_id() == 0 {
2287                return Ok(None); // anonymous group arm -- not a newtype, deferred
2288            }
2289        }
2290    }
2291    Ok(Some(template_id))
2292}
2293
2294// One method of a newtype's Reader/Builder trait: its signature (the text after `fn`) plus how a
2295// delegating impl forwards to the inherent accessor. Reader getters take `&self`, but the inherent
2296// reader accessor consumes `self` (readers are `Copy`), so they forward through `(*self)`.
2297struct TraitMethod {
2298    signature: String,
2299    call_name: String,
2300    call_args: String,
2301    deref_self: bool,
2302}
2303
2304// The Reader- or Builder-side trait methods mirroring one template field's inherent accessors:
2305// a getter (both sides), the mutators (Builder side), and `has_` for a pointer field (both sides).
2306fn newtype_trait_methods(
2307    ctx: &GeneratorContext,
2308    field: &schema_capnp::field::Reader,
2309    is_reader: bool,
2310    include_getter: bool,
2311) -> ::capnp::Result<Vec<TraitMethod>> {
2312    use capnp::schema_capnp::*;
2313    let name = camel_to_snake_case(field.get_name()?.to_str()?);
2314    let is_pointer = match field.which()? {
2315        field::Slot(s) => s.get_type()?.is_pointer()?,
2316        field::Group(_) => false,
2317    };
2318    let mut methods = Vec::new();
2319
2320    // Union arms have no inherent getter -- they are read through `which()` -- so unions pass
2321    // include_getter = false; a delegating getter would just recurse.
2322    if is_reader {
2323        if include_getter {
2324            let (ret, _, _) = getter_text(ctx, field, true, true)?;
2325            methods.push(TraitMethod {
2326                signature: format!("get_{name}(&self) {ret}"),
2327                call_name: format!("get_{name}"),
2328                call_args: String::new(),
2329                deref_self: true,
2330            });
2331        }
2332    } else {
2333        if include_getter {
2334            // Builder getters consume `self` (they hand out the child builder), matching the
2335            // inherent accessor, so no deref.
2336            let (ret, _, _) = getter_text(ctx, field, false, true)?;
2337            methods.push(TraitMethod {
2338                signature: format!("get_{name}(self) {ret}"),
2339                call_name: format!("get_{name}"),
2340                call_args: String::new(),
2341                deref_self: false,
2342            });
2343        }
2344        for m in setter_methods(ctx, 0, &name, field)? {
2345            methods.push(TraitMethod {
2346                signature: m.signature(false),
2347                call_name: m.name,
2348                call_args: m.delegate_args,
2349                deref_self: false,
2350            });
2351        }
2352    }
2353    if is_pointer {
2354        methods.push(TraitMethod {
2355            signature: format!("has_{name}(&self) -> bool"),
2356            call_name: format!("has_{name}"),
2357            call_args: String::new(),
2358            deref_self: false,
2359        });
2360    }
2361    Ok(methods)
2362}
2363
2364// The id of a `type` newtype's node, as `pub const TYPE_ID` in its module. An alias module
2365// (`type Bar = Foo`) declares its own, which takes precedence over the one its `pub use` of
2366// Foo's module brings in, so each name reports its own id. It is what
2367// `schema::Field::get_newtype_ids` returns, so code can recognise a newtype at runtime.
2368fn newtype_type_id_line(node_id: u64) -> FormattedText {
2369    Line(format!("pub const TYPE_ID: u64 = {};", format_u64(node_id)))
2370}
2371
2372// Emits the trait module for a newtype: a flat group (`generate_flat_newtype_trait`) or a union
2373// (`generate_union_newtype_trait`). Exactly one applies (or neither); each no-ops otherwise.
2374fn generate_newtype_trait(
2375    ctx: &GeneratorContext,
2376    node_id: u64,
2377    node_name: &str,
2378) -> ::capnp::Result<FormattedText> {
2379    if let Some(owner) = inline_newtype_owner(ctx, node_id)? {
2380        if owner != node_id {
2381            // `type Bar = Foo` with Foo an inline newtype: Bar's module re-exports Foo's, so
2382            // `bar::Reader` *is* `foo::Reader` and Bar's use sites implement Foo's traits.
2383            return Ok(Branch(vec![
2384                BlankLine,
2385                Line(format!("pub mod {} {{", module_name(node_name))),
2386                indent(Line(format!(
2387                    "pub use {}::*;",
2388                    ctx.get_qualified_module(owner)
2389                ))),
2390                indent(newtype_type_id_line(node_id)),
2391                line("}"),
2392            ]));
2393        }
2394    }
2395    Ok(Branch(vec![
2396        generate_group_newtype_trait(ctx, node_id, node_name)?,
2397        generate_union_newtype_trait(ctx, node_id, node_name)?,
2398    ]))
2399}
2400
2401// Recursive leaf count of a newtype template: a slot is one leaf; a nested-newtype member expands
2402// to its own leaves. The erased carrier uses this to slice its offset table.
2403fn count_newtype_leaves(ctx: &GeneratorContext, template_id: u64) -> ::capnp::Result<usize> {
2404    use capnp::schema_capnp::*;
2405    let node::Struct(tmpl) = ctx.node_map[&template_id].which()? else {
2406        return Ok(0);
2407    };
2408    let mut n = 0;
2409    for field in tmpl.get_fields()? {
2410        match field.which()? {
2411            field::Slot(_) => n += 1,
2412            field::Group(_) => {
2413                if let Some(nested) = newtype_template_id(ctx, field.get_type_id())? {
2414                    n += count_newtype_leaves(ctx, nested)?;
2415                }
2416            }
2417        }
2418    }
2419    Ok(n)
2420}
2421
2422// Collects a use site's leaf offsets in template (tree) order, recursing through nested-newtype
2423// members, matching the instance's fields to the template by name. A leaf absent from the instance
2424// (an incomplete `@[...]`) gets the sentinel `0xffff_ffff`.
2425fn collect_leaf_offsets(
2426    ctx: &GeneratorContext,
2427    template_id: u64,
2428    instance_id: u64,
2429    out: &mut Vec<String>,
2430) -> ::capnp::Result<()> {
2431    use capnp::schema_capnp::*;
2432    let node::Struct(tmpl) = ctx.node_map[&template_id].which()? else {
2433        return Ok(());
2434    };
2435    let node::Struct(instance) = ctx.node_map[&instance_id].which()? else {
2436        return Ok(());
2437    };
2438    let mut inst_fields = ::std::collections::HashMap::new();
2439    for f in instance.get_fields()? {
2440        inst_fields.insert(f.get_name()?.to_str()?.to_string(), f);
2441    }
2442    for field in tmpl.get_fields()? {
2443        let raw = field.get_name()?.to_str()?;
2444        match field.which()? {
2445            field::Slot(_) => match inst_fields.get(raw).map(|f| f.which()) {
2446                Some(Ok(field::Slot(s))) => out.push(s.get_offset().to_string()),
2447                _ => out.push("0xffff_ffff".to_string()),
2448            },
2449            field::Group(_) => {
2450                let Some(nested_tmpl) = newtype_template_id(ctx, field.get_type_id())? else {
2451                    continue;
2452                };
2453                match inst_fields.get(raw).map(|f| f.which()) {
2454                    Some(Ok(field::Group(ig))) => {
2455                        collect_leaf_offsets(ctx, nested_tmpl, ig.get_type_id(), out)?
2456                    }
2457                    _ => {
2458                        for _ in 0..count_newtype_leaves(ctx, nested_tmpl)? {
2459                            out.push("0xffff_ffff".to_string());
2460                        }
2461                    }
2462                }
2463            }
2464        }
2465    }
2466    Ok(())
2467}
2468
2469// The erased carrier for a (non-union) group newtype: a concrete `AnyReader<'a>` holding a
2470// `StructReader` plus a runtime offset table, so one type serves every use site (unlike `&dyn`).
2471// A slot accessor indexes `self.offsets[i]` via `getter_text_at`; a nested-newtype member returns
2472// the member's own `AnyReader` over a sub-slice of the table. It impls the newtype's `Reader`
2473// trait, so `impl Reader` bounds accept both the concrete per-site type and the erased form.
2474fn any_reader_items(
2475    ctx: &GeneratorContext,
2476    template_id: u64,
2477) -> ::capnp::Result<Vec<FormattedText>> {
2478    use capnp::schema_capnp::*;
2479    let node::Struct(tmpl) = ctx.node_map[&template_id].which()? else {
2480        return Ok(vec![]);
2481    };
2482    if tmpl.get_discriminant_count() > 0 {
2483        return Ok(vec![]); // union newtype -- separate carrier
2484    }
2485
2486    let mut inherent = Vec::new();
2487    let mut trait_methods = Vec::new();
2488    let mut defaults = Vec::new();
2489    let mut cursor = 0usize;
2490    for field in tmpl.get_fields()? {
2491        let name = camel_to_snake_case(field.get_name()?.to_str()?);
2492        if let field::Group(_) = field.which()? {
2493            let assoc = capitalize_first_letter(field.get_name()?.to_str()?);
2494            let nested_mod = ctx.get_qualified_module(field.get_type_id());
2495            let Some(nested_tmpl) = newtype_template_id(ctx, field.get_type_id())? else {
2496                return Ok(vec![]);
2497            };
2498            let count = count_newtype_leaves(ctx, nested_tmpl)?;
2499            let (start, end) = (cursor, cursor + count);
2500            inherent.push(Line(format!(
2501                "pub fn get_{name}(&self) -> {nested_mod}::AnyReader<'a> {{ {nested_mod}::AnyReader::new(self.reader, &self.offsets[{start}..{end}]) }}"
2502            )));
2503            trait_methods.push(Line(format!("type {assoc} = {nested_mod}::AnyReader<'a>;")));
2504            trait_methods.push(Line(format!(
2505                "fn get_{name}(&self) -> Self::{assoc} {{ self.get_{name}() }}"
2506            )));
2507            cursor += count;
2508        } else {
2509            let off = format!("self.offsets[{cursor}] as usize");
2510            let (ret, mut body, def) = getter_text_at(ctx, &field, true, true, Some(&off))?;
2511            if let Some(d) = def {
2512                defaults.push(d);
2513            }
2514            // An unmapped leaf (sentinel offset) reads its default rather than dereferencing the
2515            // sentinel -- matching C++, and 32-bit-safe (the out-of-range read would overflow).
2516            if is_scalar_data_field(&field)? {
2517                if let (field::Slot(s), FormattedText::Line(expr)) = (field.which()?, &body) {
2518                    let expr = expr.clone();
2519                    let dflt = prim_default_value(&s.get_default_value()?)?;
2520                    body = Line(format!(
2521                        "if self.offsets[{cursor}] == 0xffff_ffffu32 {{ {dflt} }} else {{ {expr} }}"
2522                    ));
2523                }
2524            }
2525            inherent.push(Line(format!("pub fn get_{name}(&self) {ret} {{")));
2526            inherent.push(indent(body));
2527            inherent.push(line("}"));
2528            trait_methods.push(Line(format!(
2529                "fn get_{name}(&self) {ret} {{ self.get_{name}() }}"
2530            )));
2531            if let field::Slot(s) = field.which()? {
2532                if s.get_type()?.is_pointer()? {
2533                    inherent.push(Line(format!(
2534                        "pub fn has_{name}(&self) -> bool {{ !self.reader.get_pointer_field(self.offsets[{cursor}] as usize).is_null() }}"
2535                    )));
2536                    trait_methods.push(Line(format!(
2537                        "fn has_{name}(&self) -> bool {{ self.has_{name}() }}"
2538                    )));
2539                }
2540            }
2541            cursor += 1;
2542        }
2543    }
2544
2545    let sr = fmt!(ctx, "{capnp}::private::layout::StructReader<'a>");
2546    let mut items = vec![
2547        BlankLine,
2548        line("#[derive(Clone, Copy)]"),
2549        Line(format!(
2550            "pub struct AnyReader<'a> {{ reader: {sr}, offsets: &'a [u32] }}"
2551        )),
2552        line("impl<'a> AnyReader<'a> {"),
2553        indent(vec![
2554            Line(format!(
2555                "pub fn new(reader: {sr}, offsets: &'a [u32]) -> Self {{ AnyReader {{ reader, offsets }} }}"
2556            )),
2557            Branch(inherent),
2558        ]),
2559        line("}"),
2560        line("impl<'a> Reader<'a> for AnyReader<'a> {"),
2561        indent(trait_methods),
2562        line("}"),
2563    ];
2564    if !defaults.is_empty() {
2565        items.push(line("mod _private {"));
2566        items.push(indent(defaults));
2567        items.push(line("}"));
2568    }
2569    Ok(items)
2570}
2571
2572// The erased carrier for a union newtype: like `AnyReader` but with a runtime discriminant offset.
2573// `which()` reads the discriminant and builds the shared `Which`, with a group arm yielding the
2574// member's own `AnyReader` over an offset sub-slice -- so the erased union unifies across use sites
2575// (which `&dyn` cannot, given the arm associated types).
2576fn union_any_reader_items(
2577    ctx: &GeneratorContext,
2578    template_id: u64,
2579) -> ::capnp::Result<Vec<FormattedText>> {
2580    use capnp::schema_capnp::*;
2581    let node::Struct(tmpl) = ctx.node_map[&template_id].which()? else {
2582        return Ok(vec![]);
2583    };
2584    if tmpl.get_discriminant_count() == 0 {
2585        return Ok(vec![]);
2586    }
2587
2588    let mut arms = Vec::new();
2589    let mut assoc = Vec::new();
2590    let mut group_variants = Vec::new();
2591    let mut which_type_params = Vec::new();
2592    let mut has_inherent = Vec::new();
2593    let mut has_trait = Vec::new();
2594    let mut needs_lifetime = false;
2595    let mut cursor = 0usize;
2596    for field in tmpl.get_fields()? {
2597        let variant = capitalize_first_letter(field.get_name()?.to_str()?);
2598        let name = camel_to_snake_case(field.get_name()?.to_str()?);
2599        let dvalue = field.get_discriminant_value();
2600        if let field::Group(_) = field.which()? {
2601            let nested_mod = ctx.get_qualified_module(field.get_type_id());
2602            let Some(nested_tmpl) = newtype_template_id(ctx, field.get_type_id())? else {
2603                return Ok(vec![]);
2604            };
2605            let count = count_newtype_leaves(ctx, nested_tmpl)?;
2606            let (start, end) = (cursor, cursor + count);
2607            arms.push(Line(format!(
2608                "{dvalue} => ::core::result::Result::Ok(Which::{variant}({nested_mod}::AnyReader::new(self.reader, &self.offsets[{start}..{end}]))),"
2609            )));
2610            assoc.push(Line(format!(
2611                "type {variant} = {nested_mod}::AnyReader<'a>;"
2612            )));
2613            group_variants.push(variant.clone());
2614            which_type_params.push(format!("{nested_mod}::AnyReader<'a>"));
2615            cursor += count;
2616        } else {
2617            let off = format!("self.offsets[{cursor}] as usize");
2618            let (_, getter_code, _) = getter_text_at(ctx, &field, true, false, Some(&off))?;
2619            let FormattedText::Line(payload) = getter_code else {
2620                return Ok(vec![]); // option-wrapped arm -- unsupported in the erased union for now
2621            };
2622            arms.push(Line(format!(
2623                "{dvalue} => ::core::result::Result::Ok(Which::{variant}({payload})),"
2624            )));
2625            if let field::Slot(s) = field.which()? {
2626                if s.get_type()?.is_pointer()? {
2627                    needs_lifetime = true;
2628                    has_inherent.push(Line(format!(
2629                        "pub fn has_{name}(&self) -> bool {{ if self.reader.get_data_field::<u16>(self.disc_offset as usize) != {dvalue} {{ return false; }} !self.reader.get_pointer_field(self.offsets[{cursor}] as usize).is_null() }}"
2630                    )));
2631                    has_trait.push(Line(format!(
2632                        "fn has_{name}(&self) -> bool {{ self.has_{name}() }}"
2633                    )));
2634                }
2635            }
2636            cursor += 1;
2637        }
2638    }
2639    arms.push(line(
2640        "x => ::core::result::Result::Err(::capnp::NotInSchema(x)),",
2641    ));
2642
2643    let inherent_ret = union_which_generics(needs_lifetime, &which_type_params);
2644    let self_ret = union_which_generics(
2645        needs_lifetime,
2646        &group_variants
2647            .iter()
2648            .map(|v| format!("Self::{v}"))
2649            .collect::<Vec<_>>(),
2650    );
2651
2652    let sr = fmt!(ctx, "{capnp}::private::layout::StructReader<'a>");
2653    let which_method = Branch(vec![
2654        Line(format!(
2655            "pub fn which(&self) -> ::core::result::Result<Which{inherent_ret}, ::capnp::NotInSchema> {{"
2656        )),
2657        indent(vec![
2658            line("match self.reader.get_data_field::<u16>(self.disc_offset as usize) {"),
2659            indent(arms),
2660            line("}"),
2661        ]),
2662        line("}"),
2663    ]);
2664
2665    let mut trait_items = assoc;
2666    trait_items.push(Line(format!(
2667        "fn which(&self) -> ::core::result::Result<Which{self_ret}, ::capnp::NotInSchema> {{ self.which() }}"
2668    )));
2669    trait_items.extend(has_trait);
2670
2671    Ok(vec![
2672        BlankLine,
2673        line("#[derive(Clone, Copy)]"),
2674        Line(format!(
2675            "pub struct AnyReader<'a> {{ reader: {sr}, disc_offset: u32, offsets: &'a [u32] }}"
2676        )),
2677        line("impl<'a> AnyReader<'a> {"),
2678        indent(vec![
2679            Line(format!(
2680                "pub fn new(reader: {sr}, disc_offset: u32, offsets: &'a [u32]) -> Self {{ AnyReader {{ reader, disc_offset, offsets }} }}"
2681            )),
2682            which_method,
2683            Branch(has_inherent),
2684        ]),
2685        line("}"),
2686        line("impl<'a> Reader<'a> for AnyReader<'a> {"),
2687        indent(trait_items),
2688        line("}"),
2689    ])
2690}
2691
2692// The erased mutable carrier for a (non-union) group newtype: `AnyBuilder<'a>` over a
2693// `StructBuilder` and a runtime offset table. Setters/init write at `self.offsets[i]`; a
2694// nested-newtype member yields the member's own `AnyBuilder` over a sub-slice.
2695fn any_builder_items(
2696    ctx: &GeneratorContext,
2697    template_id: u64,
2698) -> ::capnp::Result<Vec<FormattedText>> {
2699    use capnp::schema_capnp::*;
2700    let node::Struct(tmpl) = ctx.node_map[&template_id].which()? else {
2701        return Ok(vec![]);
2702    };
2703    if tmpl.get_discriminant_count() > 0 {
2704        return Ok(vec![]); // union AnyBuilder -- deferred
2705    }
2706
2707    let mut inherent = Vec::new();
2708    let mut trait_methods = Vec::new();
2709    let mut cursor = 0usize;
2710    for field in tmpl.get_fields()? {
2711        let name = camel_to_snake_case(field.get_name()?.to_str()?);
2712        if let field::Group(_) = field.which()? {
2713            let assoc = capitalize_first_letter(field.get_name()?.to_str()?);
2714            let nested_mod = ctx.get_qualified_module(field.get_type_id());
2715            let Some(nested_tmpl) = newtype_template_id(ctx, field.get_type_id())? else {
2716                return Ok(vec![]);
2717            };
2718            let count = count_newtype_leaves(ctx, nested_tmpl)?;
2719            let (start, end) = (cursor, cursor + count);
2720            for verb in ["get", "init"] {
2721                inherent.push(Line(format!(
2722                    "pub fn {verb}_{name}(self) -> {nested_mod}::AnyBuilder<'a> {{ {nested_mod}::AnyBuilder::new(self.builder, &self.offsets[{start}..{end}]) }}"
2723                )));
2724            }
2725            trait_methods.push(Line(format!(
2726                "type {assoc} = {nested_mod}::AnyBuilder<'a>;"
2727            )));
2728            trait_methods.push(Line(format!(
2729                "fn get_{name}(self) -> Self::{assoc} {{ self.get_{name}() }}"
2730            )));
2731            trait_methods.push(Line(format!(
2732                "fn init_{name}(self) -> Self::{assoc} {{ self.init_{name}() }}"
2733            )));
2734            cursor += count;
2735        } else {
2736            let off = format!("self.offsets[{cursor}] as usize");
2737            let guard = unmapped_panic(cursor, &name);
2738            // Builder getter: a scalar reads its default at the sentinel; a pointer getter panics
2739            // (the builder read is unchecked and there's no empty builder to return).
2740            let (ret, body, _) = getter_text_at(ctx, &field, false, true, Some(&off))?;
2741            let get_body = if is_scalar_data_field(&field)? {
2742                if let field::Slot(s) = field.which()? {
2743                    if let FormattedText::Line(expr) = &body {
2744                        let expr = expr.clone();
2745                        let dflt = prim_default_value(&s.get_default_value()?)?;
2746                        Line(format!(
2747                            "if self.offsets[{cursor}] == 0xffff_ffffu32 {{ {dflt} }} else {{ {expr} }}"
2748                        ))
2749                    } else {
2750                        body
2751                    }
2752                } else {
2753                    body
2754                }
2755            } else {
2756                Branch(vec![Line(guard.clone()), body])
2757            };
2758            inherent.push(Line(format!("pub fn get_{name}(self) {ret} {{")));
2759            inherent.push(indent(get_body));
2760            inherent.push(line("}"));
2761            trait_methods.push(Line(format!(
2762                "fn get_{name}(self) {ret} {{ self.get_{name}() }}"
2763            )));
2764            for m in setter_methods_at(ctx, "0", &name, &field, Some(&off))? {
2765                let sig_inherent = m.signature(true);
2766                let sig_trait = m.signature(false);
2767                let call = format!("self.{}({})", m.name, m.delegate_args);
2768                inherent.push(Line(format!("pub fn {sig_inherent} {{")));
2769                inherent.push(indent(Branch(vec![Line(guard.clone()), m.body])));
2770                inherent.push(line("}"));
2771                trait_methods.push(Line(format!("fn {sig_trait} {{ {call} }}")));
2772            }
2773            if let field::Slot(s) = field.which()? {
2774                if s.get_type()?.is_pointer()? {
2775                    inherent.push(Line(format!(
2776                        "pub fn has_{name}(&self) -> bool {{ if self.offsets[{cursor}] == 0xffff_ffffu32 {{ return false; }} !self.builder.is_pointer_field_null(self.offsets[{cursor}] as usize) }}"
2777                    )));
2778                    trait_methods.push(Line(format!(
2779                        "fn has_{name}(&self) -> bool {{ self.has_{name}() }}"
2780                    )));
2781                }
2782            }
2783            cursor += 1;
2784        }
2785    }
2786
2787    let sb = fmt!(ctx, "{capnp}::private::layout::StructBuilder<'a>");
2788    Ok(vec![
2789        BlankLine,
2790        Line(format!(
2791            "pub struct AnyBuilder<'a> {{ builder: {sb}, offsets: &'a [u32] }}"
2792        )),
2793        line("impl<'a> AnyBuilder<'a> {"),
2794        indent(vec![
2795            Line(format!(
2796                "pub fn new(builder: {sb}, offsets: &'a [u32]) -> Self {{ AnyBuilder {{ builder, offsets }} }}"
2797            )),
2798            Branch(inherent),
2799        ]),
2800        line("}"),
2801        line("impl<'a> Builder<'a> for AnyBuilder<'a> {"),
2802        indent(trait_methods),
2803        line("}"),
2804    ])
2805}
2806
2807// The erased mutable carrier for a union newtype: `AnyBuilder` with a runtime discriminant offset.
2808// Each arm setter writes the discriminant at `self.disc_offset` (via `setter_methods_at`); a group
2809// arm's `init_` writes it then hands out the member's own `AnyBuilder` over an offset sub-slice.
2810fn union_any_builder_items(
2811    ctx: &GeneratorContext,
2812    template_id: u64,
2813) -> ::capnp::Result<Vec<FormattedText>> {
2814    use capnp::schema_capnp::*;
2815    let node::Struct(tmpl) = ctx.node_map[&template_id].which()? else {
2816        return Ok(vec![]);
2817    };
2818    if tmpl.get_discriminant_count() == 0 {
2819        return Ok(vec![]);
2820    }
2821
2822    let mut inherent = Vec::new();
2823    let mut assoc = Vec::new();
2824    let mut trait_methods = Vec::new();
2825    let mut cursor = 0usize;
2826    for field in tmpl.get_fields()? {
2827        let variant = capitalize_first_letter(field.get_name()?.to_str()?);
2828        let name = camel_to_snake_case(field.get_name()?.to_str()?);
2829        let dvalue = field.get_discriminant_value();
2830        if let field::Group(_) = field.which()? {
2831            let nested_mod = ctx.get_qualified_module(field.get_type_id());
2832            let Some(nested_tmpl) = newtype_template_id(ctx, field.get_type_id())? else {
2833                return Ok(vec![]);
2834            };
2835            let count = count_newtype_leaves(ctx, nested_tmpl)?;
2836            let (start, end) = (cursor, cursor + count);
2837            inherent.push(Line(format!(
2838                "pub fn init_{name}(self) -> {nested_mod}::AnyBuilder<'a> {{ let b = self.builder; b.set_data_field::<u16>(self.disc_offset as usize, {dvalue}); {nested_mod}::AnyBuilder::new(b, &self.offsets[{start}..{end}]) }}"
2839            )));
2840            assoc.push(Line(format!(
2841                "type {variant} = {nested_mod}::AnyBuilder<'a>;"
2842            )));
2843            trait_methods.push(Line(format!(
2844                "fn init_{name}(self) -> Self::{variant} {{ self.init_{name}() }}"
2845            )));
2846            cursor += count;
2847        } else {
2848            let off = format!("self.offsets[{cursor}] as usize");
2849            let guard = unmapped_panic(cursor, &name);
2850            for m in setter_methods_at(ctx, "self.disc_offset as usize", &name, &field, Some(&off))?
2851            {
2852                let sig_inherent = m.signature(true);
2853                let sig_trait = m.signature(false);
2854                let call = format!("self.{}({})", m.name, m.delegate_args);
2855                inherent.push(Line(format!("pub fn {sig_inherent} {{")));
2856                inherent.push(indent(Branch(vec![Line(guard.clone()), m.body])));
2857                inherent.push(line("}"));
2858                trait_methods.push(Line(format!("fn {sig_trait} {{ {call} }}")));
2859            }
2860            if let field::Slot(s) = field.which()? {
2861                if s.get_type()?.is_pointer()? {
2862                    inherent.push(Line(format!(
2863                        "pub fn has_{name}(&self) -> bool {{ if self.builder.get_data_field::<u16>(self.disc_offset as usize) != {dvalue} {{ return false; }} !self.builder.is_pointer_field_null(self.offsets[{cursor}] as usize) }}"
2864                    )));
2865                    trait_methods.push(Line(format!(
2866                        "fn has_{name}(&self) -> bool {{ self.has_{name}() }}"
2867                    )));
2868                }
2869            }
2870            cursor += 1;
2871        }
2872    }
2873
2874    let sb = fmt!(ctx, "{capnp}::private::layout::StructBuilder<'a>");
2875    let mut trait_items = assoc;
2876    trait_items.extend(trait_methods);
2877    Ok(vec![
2878        BlankLine,
2879        Line(format!(
2880            "pub struct AnyBuilder<'a> {{ builder: {sb}, disc_offset: u32, offsets: &'a [u32] }}"
2881        )),
2882        line("impl<'a> AnyBuilder<'a> {"),
2883        indent(vec![
2884            Line(format!(
2885                "pub fn new(builder: {sb}, disc_offset: u32, offsets: &'a [u32]) -> Self {{ AnyBuilder {{ builder, disc_offset, offsets }} }}"
2886            )),
2887            Branch(inherent),
2888        ]),
2889        line("}"),
2890        line("impl<'a> Builder<'a> for AnyBuilder<'a> {"),
2891        indent(trait_items),
2892        line("}"),
2893    ])
2894}
2895
2896// `capnp::traits::AnyReader` / `AnyBuilder` for a newtype's erased carriers, plus the
2897// `DowncastReader` / `DowncastBuilder` impls through which the dynamic API reaches them:
2898// `group.downcast::<vec3::AnyReader>()`. The downcast reads the use site's offset table from the
2899// group's schema (see `use_site_any_layout`) and checks it against `TYPE_ID` and `LEAF_COUNT`.
2900fn any_downcast_impls(
2901    ctx: &GeneratorContext,
2902    template_id: u64,
2903    is_union: bool,
2904    has_reader: bool,
2905    has_builder: bool,
2906) -> ::capnp::Result<Vec<FormattedText>> {
2907    let leaves = count_newtype_leaves(ctx, template_id)?;
2908    // A union carrier's `new` takes the discriminant offset; a group carrier's doesn't.
2909    let disc_arg = if is_union {
2910        "discriminant_offset, "
2911    } else {
2912        ""
2913    };
2914    let mut items = Vec::new();
2915    let mut emit = |kind: &str, raw: &str, value: &str, downcast: &str| {
2916        let ident = kind.to_ascii_lowercase();
2917        let unused = if is_union { "" } else { "_" };
2918        items.push(Line(fmt!(
2919            ctx,
2920            "impl<'a> {capnp}::traits::{kind}<'a> for {kind}<'a> {{"
2921        )));
2922        items.push(indent(vec![
2923            line("const TYPE_ID: u64 = TYPE_ID;"),
2924            Line(format!("const LEAF_COUNT: usize = {leaves};")),
2925            Line(fmt!(ctx,
2926                "fn from_use_site({ident}: {capnp}::private::layout::{raw}<'a>, offsets: &'a [u32], {unused}discriminant_offset: u32) -> Self {{ Self::new({ident}, {disc_arg}offsets) }}"
2927            )),
2928        ]));
2929        items.push(line("}"));
2930        items.push(Line(fmt!(
2931            ctx,
2932            "impl<'a> {capnp}::dynamic_value::Downcast{value}<'a> for {kind}<'a> {{"
2933        )));
2934        items.push(indent(Line(fmt!(ctx,
2935            "fn downcast_{value_lc}(v: {capnp}::dynamic_value::{value}<'a>) -> Self {{ {capnp}::dynamic_value::{downcast}(v) }}",
2936            value_lc = value.to_ascii_lowercase()
2937        ))));
2938        items.push(line("}"));
2939    };
2940    if has_reader {
2941        emit("AnyReader", "StructReader", "Reader", "downcast_any_reader");
2942    }
2943    if has_builder {
2944        emit(
2945            "AnyBuilder",
2946            "StructBuilder",
2947            "Builder",
2948            "downcast_any_builder",
2949        );
2950    }
2951    Ok(items)
2952}
2953
2954// For a group that is a use site of a group or union newtype, the `.with_any_layout(..)` call that
2955// records the newtype ids and offset table on its `RAW_SCHEMA`, so the dynamic API can downcast it.
2956// The offset table is the one the parent's module already emits for `as_any()` (named by
2957// `use_site_offsets_table`); a private item of an ancestor module is visible here. None for any
2958// other node.
2959fn use_site_any_layout(
2960    ctx: &GeneratorContext,
2961    node: schema_capnp::node::Reader,
2962) -> ::capnp::Result<Option<String>> {
2963    use capnp::schema_capnp::*;
2964    let node::Struct(instance) = node.which()? else {
2965        return Ok(None);
2966    };
2967    if !instance.get_is_group() {
2968        return Ok(None);
2969    }
2970    let id = node.get_id();
2971    let Some(parent) = ctx.node_map.get(&node.get_scope_id()) else {
2972        return Ok(None);
2973    };
2974    let node::Struct(parent_struct) = parent.which()? else {
2975        return Ok(None);
2976    };
2977    for field in parent_struct.get_fields()? {
2978        let field::Group(group) = field.which()? else {
2979            continue;
2980        };
2981        if group.get_type_id() != id || field.get_type_id() == 0 {
2982            continue;
2983        }
2984        let alias_id = field.get_type_id();
2985        let is_newtype = group_newtype_template_id(ctx, alias_id)?.is_some()
2986            || union_newtype_template_id(ctx, alias_id)?.is_some();
2987        if !is_newtype {
2988            return Ok(None);
2989        }
2990        let ids: Vec<String> = field_newtype_ids(ctx, field)?
2991            .into_iter()
2992            .map(format_u64)
2993            .collect();
2994        let table = use_site_offsets_table(ctx, node.get_scope_id(), id)?;
2995        return Ok(Some(format!(
2996            ".with_any_layout(&[{}], &{table}, {})",
2997            ids.join(", "),
2998            instance.get_discriminant_offset()
2999        )));
3000    }
3001    Ok(None)
3002}
3003
3004// The path of the `*_OFFSETS` static for the use-site group `instance_id`, emitted in the module of
3005// its parent struct `parent_id`.
3006fn use_site_offsets_table(
3007    ctx: &GeneratorContext,
3008    parent_id: u64,
3009    instance_id: u64,
3010) -> ::capnp::Result<String> {
3011    Ok(format!(
3012        "{}::{}",
3013        ctx.get_qualified_module(parent_id),
3014        use_site_offsets_name(ctx, instance_id)?
3015    ))
3016}
3017
3018fn use_site_offsets_name(ctx: &GeneratorContext, instance_id: u64) -> ::capnp::Result<String> {
3019    Ok(format!(
3020        "{}_OFFSETS",
3021        camel_to_snake_case(ctx.get_last_name(instance_id)?).to_ascii_uppercase()
3022    ))
3023}
3024
3025// Emits `pub mod <name> { pub trait Reader<'a> {...} pub trait Builder<'a> {...} }` for a non-union
3026// group newtype. Slot fields mirror the per-use-site accessors; a nested-newtype member (a group
3027// field) is surfaced as an associated type bounded by that newtype's trait, plus a getter.
3028fn generate_group_newtype_trait(
3029    ctx: &GeneratorContext,
3030    node_id: u64,
3031    node_name: &str,
3032) -> ::capnp::Result<FormattedText> {
3033    use capnp::schema_capnp::*;
3034    let Some(template_id) = group_newtype_template_id(ctx, node_id)? else {
3035        return Ok(Branch(vec![]));
3036    };
3037    let node::Struct(tmpl) = ctx.node_map[&template_id].which()? else {
3038        return Ok(Branch(vec![]));
3039    };
3040
3041    let mut reader_items = Vec::new();
3042    let mut builder_items = Vec::new();
3043    for field in tmpl.get_fields()? {
3044        if let field::Group(_) = field.which()? {
3045            // Nested newtype member: an associated type bounded by the member's trait, plus a
3046            // getter (and builder init) -- no whole-value setter, same as a struct field.
3047            let name = camel_to_snake_case(field.get_name()?.to_str()?);
3048            let assoc = capitalize_first_letter(field.get_name()?.to_str()?);
3049            let nested = ctx.get_qualified_module(field.get_type_id());
3050            reader_items.push(Line(format!("type {assoc}: {nested}::Reader<'a>;")));
3051            reader_items.push(Line(format!("fn get_{name}(&self) -> Self::{assoc};")));
3052            builder_items.push(Line(format!("type {assoc}: {nested}::Builder<'a>;")));
3053            builder_items.push(Line(format!("fn get_{name}(self) -> Self::{assoc};")));
3054            builder_items.push(Line(format!("fn init_{name}(self) -> Self::{assoc};")));
3055        } else {
3056            for m in newtype_trait_methods(ctx, &field, true, true)? {
3057                reader_items.push(Line(format!("fn {};", m.signature)));
3058            }
3059            for m in newtype_trait_methods(ctx, &field, false, true)? {
3060                builder_items.push(Line(format!("fn {};", m.signature)));
3061            }
3062        }
3063    }
3064
3065    let mut module_items = vec![
3066        line("pub trait Reader<'a> {"),
3067        indent(reader_items),
3068        line("}"),
3069        line("pub trait Builder<'a> {"),
3070        indent(builder_items),
3071        line("}"),
3072    ];
3073    let any_reader = any_reader_items(ctx, template_id)?;
3074    let any_builder = any_builder_items(ctx, template_id)?;
3075    let downcasts = any_downcast_impls(
3076        ctx,
3077        template_id,
3078        false,
3079        !any_reader.is_empty(),
3080        !any_builder.is_empty(),
3081    )?;
3082    module_items.extend(any_reader);
3083    module_items.extend(any_builder);
3084    module_items.extend(downcasts);
3085
3086    Ok(Branch(vec![
3087        BlankLine,
3088        Line(format!("pub mod {} {{", module_name(node_name))),
3089        indent(newtype_type_id_line(node_id)),
3090        indent(module_items),
3091        line("}"),
3092    ]))
3093}
3094
3095// Emits `impl<'a> <newtype>::Reader<'a> for <instance>::Reader<'a>` at a use site, giving the
3096// stamped group's per-instance module the newtype's identity. Each method delegates to the
3097// inherent accessor (inherent methods win over trait methods in method resolution, so this is
3098// not recursive), which keeps offsets/defaults/pointer handling in one place.
3099fn generate_newtype_impl(
3100    ctx: &GeneratorContext,
3101    alias_id: u64,
3102    instance_id: u64,
3103) -> ::capnp::Result<FormattedText> {
3104    Ok(Branch(vec![
3105        generate_group_newtype_impl(ctx, alias_id, instance_id)?,
3106        generate_union_newtype_impl(ctx, alias_id, instance_id)?,
3107    ]))
3108}
3109
3110fn generate_group_newtype_impl(
3111    ctx: &GeneratorContext,
3112    alias_id: u64,
3113    instance_id: u64,
3114) -> ::capnp::Result<FormattedText> {
3115    use capnp::schema_capnp::*;
3116    let Some(template_id) = group_newtype_template_id(ctx, alias_id)? else {
3117        return Ok(Branch(vec![]));
3118    };
3119    let node::Struct(tmpl) = ctx.node_map[&template_id].which()? else {
3120        return Ok(Branch(vec![]));
3121    };
3122    let node::Struct(instance) = ctx.node_map[&instance_id].which()? else {
3123        return Ok(Branch(vec![]));
3124    };
3125
3126    // An incomplete `@[...]` mapping omits trailing fields from the instance; those have no
3127    // inherent accessor to delegate to, so skip the trait for such a use site until the erased
3128    // carrier (which reads defaults) lands. Index the instance's fields by name so nested members
3129    // can find their per-instance group node.
3130    let mut instance_fields = ::std::collections::HashMap::new();
3131    for field in instance.get_fields()? {
3132        instance_fields.insert(field.get_name()?.to_str()?.to_string(), field);
3133    }
3134
3135    let mut trait_ok = true;
3136    let mut reader_items = Vec::new();
3137    let mut builder_items = Vec::new();
3138    for field in tmpl.get_fields()? {
3139        let raw_name = field.get_name()?.to_str()?;
3140        let Some(inst_field) = instance_fields.get(raw_name) else {
3141            // Unmapped leaf (incomplete `@[...]`): a scalar reads its default and its setter
3142            // panics -- matching C++'s default read + "not mapped" assert. Anything else can't be
3143            // synthesized in the concrete trait here, so skip just the trait for this use site --
3144            // the erased `AnyReader` (via the offset table's sentinels) still reads it.
3145            if is_scalar_data_field(&field)? {
3146                let field::Slot(s) = field.which()? else {
3147                    trait_ok = false;
3148                    continue;
3149                };
3150                let dflt = prim_default_value(&s.get_default_value()?)?;
3151                let not_mapped =
3152                    format!("panic!(\"field '{raw_name}' is not mapped at this use site\")");
3153                for m in newtype_trait_methods(ctx, &field, true, true)? {
3154                    reader_items.push(Line(format!("fn {} {{ {dflt} }}", m.signature)));
3155                }
3156                for m in newtype_trait_methods(ctx, &field, false, true)? {
3157                    let body = if m.call_name.starts_with("get_") {
3158                        dflt.clone()
3159                    } else if m.call_args.is_empty() {
3160                        not_mapped.clone()
3161                    } else if m.call_args.contains(',') {
3162                        format!("let _ = ({}); {not_mapped}", m.call_args)
3163                    } else {
3164                        // Consume the arg so the panic-only body doesn't warn as unused.
3165                        format!("let _ = {}; {not_mapped}", m.call_args)
3166                    };
3167                    builder_items.push(Line(format!("fn {} {{ {body} }}", m.signature)));
3168                }
3169            } else {
3170                trait_ok = false;
3171            }
3172            continue;
3173        };
3174        // Delegate to the inherent accessor: inherent methods win over trait methods in method
3175        // resolution, so this forwards rather than recurses, keeping offsets/defaults in one place.
3176        if let field::Group(_) = field.which()? {
3177            let field::Group(inst_group) = inst_field.which()? else {
3178                return Ok(Branch(vec![]));
3179            };
3180            let name = camel_to_snake_case(raw_name);
3181            let assoc = capitalize_first_letter(raw_name);
3182            let inst_mod = ctx.get_qualified_module(inst_group.get_type_id());
3183            reader_items.push(Line(format!("type {assoc} = {inst_mod}::Reader<'a>;")));
3184            reader_items.push(Line(format!(
3185                "fn get_{name}(&self) -> Self::{assoc} {{ (*self).get_{name}() }}"
3186            )));
3187            builder_items.push(Line(format!("type {assoc} = {inst_mod}::Builder<'a>;")));
3188            builder_items.push(Line(format!(
3189                "fn get_{name}(self) -> Self::{assoc} {{ self.get_{name}() }}"
3190            )));
3191            builder_items.push(Line(format!(
3192                "fn init_{name}(self) -> Self::{assoc} {{ self.init_{name}() }}"
3193            )));
3194        } else {
3195            for m in newtype_trait_methods(ctx, &field, true, true)? {
3196                let recv = if m.deref_self { "(*self)" } else { "self" };
3197                reader_items.push(Line(format!(
3198                    "fn {} {{ {recv}.{}({}) }}",
3199                    m.signature, m.call_name, m.call_args
3200                )));
3201            }
3202            for m in newtype_trait_methods(ctx, &field, false, true)? {
3203                let recv = if m.deref_self { "(*self)" } else { "self" };
3204                builder_items.push(Line(format!(
3205                    "fn {} {{ {recv}.{}({}) }}",
3206                    m.signature, m.call_name, m.call_args
3207                )));
3208            }
3209        }
3210    }
3211
3212    let reader_methods = reader_items;
3213    let builder_methods = builder_items;
3214    let trait_path = ctx.get_qualified_module(alias_id);
3215    let concrete = ctx.get_qualified_module(instance_id);
3216
3217    // Erased-carrier wiring: a per-use-site offset table (recursively over nested members) plus
3218    // `as_any()` on the concrete reader -- through the public `IntoInternalStructReader`, since the
3219    // inner field is private at this impl site.
3220    let mut any_items = Vec::new();
3221    {
3222        let mut offsets = Vec::new();
3223        collect_leaf_offsets(ctx, template_id, instance_id, &mut offsets)?;
3224        let n = offsets.len();
3225        let table = use_site_offsets_name(ctx, instance_id)?;
3226        any_items.push(BlankLine);
3227        any_items.push(Line(format!(
3228            "static {table}: [u32; {n}] = [{}];",
3229            offsets.join(", ")
3230        )));
3231        any_items.push(Line(format!("impl<'a> {concrete}::Reader<'a> {{")));
3232        any_items.push(indent(Line(fmt!(ctx,
3233            "pub fn as_any(self) -> {trait_path}::AnyReader<'a> {{ {trait_path}::AnyReader::new({capnp}::traits::IntoInternalStructReader::into_internal_struct_reader(self), &{table}) }}"
3234        ))));
3235        any_items.push(line("}"));
3236        any_items.push(Line(format!("impl<'a> {concrete}::Builder<'a> {{")));
3237        any_items.push(indent(Line(fmt!(ctx,
3238            "pub fn as_any(self) -> {trait_path}::AnyBuilder<'a> {{ {trait_path}::AnyBuilder::new({capnp}::traits::IntoInternalStructBuilder::into_internal_struct_builder(self), &{table}) }}"
3239        ))));
3240        any_items.push(line("}"));
3241    }
3242
3243    // The concrete trait impls require every leaf mappable (see `trait_ok`); the erased carrier
3244    // (`any_items`) is always emitted -- it reads unmapped leaves as defaults via the offset table.
3245    let mut out = Vec::new();
3246    if trait_ok {
3247        out.push(BlankLine);
3248        out.push(Line(format!(
3249            "impl<'a> {trait_path}::Reader<'a> for {concrete}::Reader<'a> {{"
3250        )));
3251        out.push(indent(reader_methods));
3252        out.push(line("}"));
3253        out.push(BlankLine);
3254        out.push(Line(format!(
3255            "impl<'a> {trait_path}::Builder<'a> for {concrete}::Builder<'a> {{"
3256        )));
3257        out.push(indent(builder_methods));
3258        out.push(line("}"));
3259    }
3260    out.push(Branch(any_items));
3261    Ok(Branch(out))
3262}
3263
3264// The `Which` enum variants for a union newtype and whether any arm payload borrows (so the enum
3265// needs a `<'a>`). Payload types come straight from the arm getter, so they match the per-instance
3266// `Which` the impl maps from.
3267// The `<...>` on a union newtype's `Which`: an optional `'a` (when a slot arm's payload borrows)
3268// followed by one type parameter per group arm (`params`).
3269fn union_which_generics(needs_lifetime: bool, params: &[String]) -> String {
3270    let mut all = Vec::new();
3271    if needs_lifetime {
3272        all.push("'a".to_string());
3273    }
3274    all.extend(params.iter().cloned());
3275    if all.is_empty() {
3276        String::new()
3277    } else {
3278        format!("<{}>", all.join(", "))
3279    }
3280}
3281
3282// Emits `pub mod <name> { pub enum Which<...> {...} pub trait Reader<'a> {...} pub trait Builder }`
3283// for a union newtype. A slot arm has a concrete `Which` payload; a group arm (a nested newtype) is
3284// a `Which` type parameter backed by an associated type bounded by the arm's trait.
3285fn generate_union_newtype_trait(
3286    ctx: &GeneratorContext,
3287    node_id: u64,
3288    node_name: &str,
3289) -> ::capnp::Result<FormattedText> {
3290    use capnp::schema_capnp::*;
3291    let Some(template_id) = union_newtype_template_id(ctx, node_id)? else {
3292        return Ok(Branch(vec![]));
3293    };
3294    let node::Struct(tmpl) = ctx.node_map[&template_id].which()? else {
3295        return Ok(Branch(vec![]));
3296    };
3297
3298    let mut variants = Vec::new();
3299    let mut group_params = Vec::new();
3300    let mut reader_assoc = Vec::new();
3301    let mut reader_methods = Vec::new();
3302    let mut builder_assoc = Vec::new();
3303    let mut builder_methods = Vec::new();
3304    let mut needs_lifetime = false;
3305    for field in tmpl.get_fields()? {
3306        let variant = capitalize_first_letter(field.get_name()?.to_str()?);
3307        let name = camel_to_snake_case(field.get_name()?.to_str()?);
3308        if let field::Group(_) = field.which()? {
3309            let nested = ctx.get_qualified_module(field.get_type_id());
3310            variants.push(Line(format!("{variant}({variant}),")));
3311            group_params.push(variant.clone());
3312            reader_assoc.push(Line(format!("type {variant}: {nested}::Reader<'a>;")));
3313            builder_assoc.push(Line(format!("type {variant}: {nested}::Builder<'a>;")));
3314            builder_methods.push(Line(format!("fn init_{name}(self) -> Self::{variant};")));
3315        } else {
3316            let (payload, _, _) = getter_text(ctx, &field, true, false)?;
3317            if let field::Slot(s) = field.which()? {
3318                if s.get_type()?.is_pointer()? {
3319                    needs_lifetime = true;
3320                }
3321            }
3322            variants.push(Line(format!("{variant}({payload}),")));
3323            for m in newtype_trait_methods(ctx, &field, true, false)? {
3324                reader_methods.push(Line(format!("fn {};", m.signature)));
3325            }
3326            for m in newtype_trait_methods(ctx, &field, false, false)? {
3327                builder_methods.push(Line(format!("fn {};", m.signature)));
3328            }
3329        }
3330    }
3331
3332    let which_generics = union_which_generics(needs_lifetime, &group_params);
3333    let self_params: Vec<String> = group_params.iter().map(|p| format!("Self::{p}")).collect();
3334    let which_ret = union_which_generics(needs_lifetime, &self_params);
3335
3336    let mut reader_items = reader_assoc;
3337    reader_items.push(Line(format!(
3338        "fn which(&self) -> ::core::result::Result<Which{which_ret}, ::capnp::NotInSchema>;"
3339    )));
3340    reader_items.extend(reader_methods);
3341    let mut builder_items = builder_assoc;
3342    builder_items.extend(builder_methods);
3343
3344    let mut module_items = vec![
3345        Branch(vec![
3346            Line(format!("pub enum Which{which_generics} {{")),
3347            indent(variants),
3348            line("}"),
3349        ]),
3350        line("pub trait Reader<'a> {"),
3351        indent(reader_items),
3352        line("}"),
3353        line("pub trait Builder<'a> {"),
3354        indent(builder_items),
3355        line("}"),
3356    ];
3357    let any_reader = union_any_reader_items(ctx, template_id)?;
3358    let any_builder = union_any_builder_items(ctx, template_id)?;
3359    let downcasts = any_downcast_impls(
3360        ctx,
3361        template_id,
3362        true,
3363        !any_reader.is_empty(),
3364        !any_builder.is_empty(),
3365    )?;
3366    module_items.extend(any_reader);
3367    module_items.extend(any_builder);
3368    module_items.extend(downcasts);
3369
3370    Ok(Branch(vec![
3371        BlankLine,
3372        Line(format!("pub mod {} {{", module_name(node_name))),
3373        indent(newtype_type_id_line(node_id)),
3374        indent(module_items),
3375        line("}"),
3376    ]))
3377}
3378
3379// Impls the union newtype's Reader/Builder traits for a use site. `which()` maps the per-instance
3380// `Which` variants to the shared enum; the arm accessors delegate to the inherent methods (which
3381// carry the right discriminant offset).
3382fn generate_union_newtype_impl(
3383    ctx: &GeneratorContext,
3384    alias_id: u64,
3385    instance_id: u64,
3386) -> ::capnp::Result<FormattedText> {
3387    use capnp::schema_capnp::*;
3388    let Some(template_id) = union_newtype_template_id(ctx, alias_id)? else {
3389        return Ok(Branch(vec![]));
3390    };
3391    let node::Struct(tmpl) = ctx.node_map[&template_id].which()? else {
3392        return Ok(Branch(vec![]));
3393    };
3394    let node::Struct(instance) = ctx.node_map[&instance_id].which()? else {
3395        return Ok(Branch(vec![]));
3396    };
3397
3398    let mut instance_fields = ::std::collections::HashMap::new();
3399    for field in instance.get_fields()? {
3400        instance_fields.insert(field.get_name()?.to_str()?.to_string(), field);
3401    }
3402
3403    let trait_path = ctx.get_qualified_module(alias_id);
3404    let concrete = ctx.get_qualified_module(instance_id);
3405
3406    let mut which_arms = Vec::new();
3407    let mut group_params = Vec::new();
3408    let mut reader_assoc = Vec::new();
3409    let mut reader_methods = Vec::new();
3410    let mut builder_assoc = Vec::new();
3411    let mut builder_methods = Vec::new();
3412    let mut needs_lifetime = false;
3413    for field in tmpl.get_fields()? {
3414        let raw_name = field.get_name()?.to_str()?;
3415        let Some(inst_field) = instance_fields.get(raw_name) else {
3416            return Ok(Branch(vec![]));
3417        };
3418        let variant = capitalize_first_letter(raw_name);
3419        let name = camel_to_snake_case(raw_name);
3420        which_arms.push(Line(format!(
3421            "{concrete}::Which::{variant}(x) => {trait_path}::Which::{variant}(x),"
3422        )));
3423        if let field::Group(_) = field.which()? {
3424            let field::Group(inst_group) = inst_field.which()? else {
3425                return Ok(Branch(vec![]));
3426            };
3427            let inst_mod = ctx.get_qualified_module(inst_group.get_type_id());
3428            group_params.push(variant.clone());
3429            reader_assoc.push(Line(format!("type {variant} = {inst_mod}::Reader<'a>;")));
3430            builder_assoc.push(Line(format!("type {variant} = {inst_mod}::Builder<'a>;")));
3431            builder_methods.push(Line(format!(
3432                "fn init_{name}(self) -> Self::{variant} {{ self.init_{name}() }}"
3433            )));
3434        } else {
3435            if let field::Slot(s) = field.which()? {
3436                if s.get_type()?.is_pointer()? {
3437                    needs_lifetime = true;
3438                }
3439            }
3440            for m in newtype_trait_methods(ctx, &field, true, false)? {
3441                let recv = if m.deref_self { "(*self)" } else { "self" };
3442                reader_methods.push(Line(format!(
3443                    "fn {} {{ {recv}.{}({}) }}",
3444                    m.signature, m.call_name, m.call_args
3445                )));
3446            }
3447            for m in newtype_trait_methods(ctx, &field, false, false)? {
3448                let recv = if m.deref_self { "(*self)" } else { "self" };
3449                builder_methods.push(Line(format!(
3450                    "fn {} {{ {recv}.{}({}) }}",
3451                    m.signature, m.call_name, m.call_args
3452                )));
3453            }
3454        }
3455    }
3456    let self_params: Vec<String> = group_params.iter().map(|p| format!("Self::{p}")).collect();
3457    let which_ret = union_which_generics(needs_lifetime, &self_params);
3458
3459    let which_method = Branch(vec![
3460        Line(format!(
3461            "fn which(&self) -> ::core::result::Result<{trait_path}::Which{which_ret}, ::capnp::NotInSchema> {{"
3462        )),
3463        indent(vec![
3464            line("::core::result::Result::Ok(match (*self).which()? {"),
3465            indent(which_arms),
3466            line("})"),
3467        ]),
3468        line("}"),
3469    ]);
3470
3471    let mut reader_items = reader_assoc;
3472    reader_items.push(which_method);
3473    reader_items.extend(reader_methods);
3474    let mut builder_items = builder_assoc;
3475    builder_items.extend(builder_methods);
3476
3477    // Erased-carrier wiring: the offset table plus a runtime discriminant offset, then `as_any()`.
3478    let mut offsets = Vec::new();
3479    collect_leaf_offsets(ctx, template_id, instance_id, &mut offsets)?;
3480    let n = offsets.len();
3481    let disc = instance.get_discriminant_offset();
3482    let table = use_site_offsets_name(ctx, instance_id)?;
3483    let any_items = vec![
3484        BlankLine,
3485        Line(format!(
3486            "static {table}: [u32; {n}] = [{}];",
3487            offsets.join(", ")
3488        )),
3489        Line(format!("impl<'a> {concrete}::Reader<'a> {{")),
3490        indent(Line(fmt!(ctx,
3491            "pub fn as_any(self) -> {trait_path}::AnyReader<'a> {{ {trait_path}::AnyReader::new({capnp}::traits::IntoInternalStructReader::into_internal_struct_reader(self), {disc}, &{table}) }}"
3492        ))),
3493        line("}"),
3494        Line(format!("impl<'a> {concrete}::Builder<'a> {{")),
3495        indent(Line(fmt!(ctx,
3496            "pub fn as_any(self) -> {trait_path}::AnyBuilder<'a> {{ {trait_path}::AnyBuilder::new({capnp}::traits::IntoInternalStructBuilder::into_internal_struct_builder(self), {disc}, &{table}) }}"
3497        ))),
3498        line("}"),
3499    ];
3500
3501    Ok(Branch(vec![
3502        BlankLine,
3503        Line(format!(
3504            "impl<'a> {trait_path}::Reader<'a> for {concrete}::Reader<'a> {{"
3505        )),
3506        indent(reader_items),
3507        line("}"),
3508        BlankLine,
3509        Line(format!(
3510            "impl<'a> {trait_path}::Builder<'a> for {concrete}::Builder<'a> {{"
3511        )),
3512        indent(builder_items),
3513        line("}"),
3514        Branch(any_items),
3515    ]))
3516}
3517
3518// Emits `pub mod <name> { pub type Reader ...; Builder; Owned }` for a scalar newtype (`type Uuid
3519// = Data`, `type Age = UInt16`) so use sites can name the alias. A value newtype's aliases carry
3520// no lifetime; a pointer newtype's Reader/Builder keep `<'a>`. Inline group/union newtypes (which
3521// get a trait instead) and aliases of a pre-existing struct/interface are skipped here.
3522fn generate_scalar_newtype_alias(
3523    ctx: &GeneratorContext,
3524    node_id: u64,
3525    node_name: &str,
3526    t: schema_capnp::type_::Reader,
3527) -> ::capnp::Result<FormattedText> {
3528    use capnp::schema_capnp::*;
3529    if newtype_template_id(ctx, node_id)?.is_some() {
3530        return Ok(Branch(vec![]));
3531    }
3532    if matches!(t.which()?, type_::Struct(_) | type_::Interface(_)) {
3533        return Ok(Branch(vec![]));
3534    }
3535    let module = module_name(node_name);
3536    let items = if t.is_pointer()? {
3537        vec![
3538            Line(format!(
3539                "pub type Reader<'a> = {};",
3540                t.type_string(ctx, Leaf::Reader("'a"))?
3541            )),
3542            Line(format!(
3543                "pub type Builder<'a> = {};",
3544                t.type_string(ctx, Leaf::Builder("'a"))?
3545            )),
3546            Line(format!(
3547                "pub type Owned = {};",
3548                t.type_string(ctx, Leaf::Owned)?
3549            )),
3550        ]
3551    } else {
3552        let v = t.type_string(ctx, Leaf::Reader(""))?;
3553        vec![
3554            Line(format!("pub type Reader = {v};")),
3555            Line(format!("pub type Builder = {v};")),
3556            Line(format!("pub type Owned = {v};")),
3557        ]
3558    };
3559    Ok(Branch(vec![
3560        BlankLine,
3561        Line(format!("pub mod {module} {{")),
3562        indent(newtype_type_id_line(node_id)),
3563        indent(items),
3564        line("}"),
3565    ]))
3566}
3567
3568fn generate_node(
3569    ctx: &GeneratorContext,
3570    node_id: u64,
3571    node_name: &str,
3572) -> ::capnp::Result<FormattedText> {
3573    use capnp::schema_capnp::*;
3574
3575    let mut output: Vec<FormattedText> = Vec::new();
3576    let mut nested_output: Vec<FormattedText> = Vec::new();
3577
3578    let node_reader = &ctx.node_map[&node_id];
3579    let nested_nodes = node_reader.get_nested_nodes()?;
3580    for nested_node in nested_nodes {
3581        let id = nested_node.get_id();
3582        nested_output.push(generate_node(ctx, id, ctx.get_last_name(id)?)?);
3583    }
3584
3585    match node_reader.which()? {
3586        node::Type(Ok(t)) => {
3587            output.push(generate_newtype_trait(ctx, node_id, node_name)?);
3588            output.push(generate_scalar_newtype_alias(ctx, node_id, node_name, t)?);
3589        }
3590        node::Type(Err(e)) => {
3591            return Err(Error::failed(format!("error reading node type: {e}")));
3592        }
3593        node::File(()) => {
3594            output.push(Branch(nested_output));
3595        }
3596        node::Struct(struct_reader) => {
3597            let params = node_reader.parameters_texts(ctx);
3598            output.push(BlankLine);
3599
3600            let is_generic = node_reader.get_is_generic();
3601            if is_generic {
3602                output.push(Line(format!(
3603                    "pub mod {} {{ /* {} */",
3604                    node_name,
3605                    params.expanded_list.join(",")
3606                )));
3607            } else {
3608                output.push(Line(format!("pub mod {node_name} {{")));
3609            }
3610            let bracketed_params = if params.params.is_empty() {
3611                "".to_string()
3612            } else {
3613                format!("<{}>", params.params)
3614            };
3615
3616            let mut preamble = Vec::new();
3617            let mut builder_members = Vec::new();
3618            let mut reader_members = Vec::new();
3619            let mut union_fields = Vec::new();
3620            let mut which_enums = Vec::new();
3621            let mut pipeline_impl_interior = Vec::new();
3622            let mut private_mod_interior = Vec::new();
3623
3624            let data_size = struct_reader.get_data_word_count();
3625            let pointer_size = struct_reader.get_pointer_count();
3626            let discriminant_count = struct_reader.get_discriminant_count();
3627            let discriminant_offset = struct_reader.get_discriminant_offset();
3628
3629            private_mod_interior.push(crate::pointer_constants::node_word_array_declaration(
3630                ctx,
3631                "ENCODED_NODE",
3632                *node_reader,
3633                crate::pointer_constants::WordArrayDeclarationOptions { pub_crate: true },
3634            )?);
3635
3636            private_mod_interior.push(generate_get_field_types(ctx, *node_reader)?);
3637            private_mod_interior.push(generate_get_annotation_types(ctx, *node_reader)?);
3638
3639            // `static` instead of `const` so that this has a fixed memory address
3640            // and we can check equality of `RawStructSchema` values by comparing pointers.
3641            let field_newtypes = generate_field_newtypes(ctx, *node_reader)?;
3642            let mut raw_schema_end = ")".to_string();
3643            if field_newtypes.is_some() {
3644                raw_schema_end += ".with_field_newtypes(FIELD_NEWTYPES)";
3645            }
3646            if let Some(any_layout) = use_site_any_layout(ctx, *node_reader)? {
3647                raw_schema_end += &any_layout;
3648            }
3649            raw_schema_end += ";";
3650            private_mod_interior.push(Branch(vec![
3651                Line(fmt!(ctx, "pub(crate) static ARENA: {capnp}::private::arena::GeneratedCodeArena = {capnp}::private::arena::GeneratedCodeArena::new(&ENCODED_NODE);")),
3652                Line(fmt!(ctx,"pub(crate) static RAW_SCHEMA: {capnp}::introspect::RawStructSchema = {capnp}::introspect::RawStructSchema::new(")),
3653                indent(vec![
3654                    Line("&ARENA,".into()),
3655                    Line("NONUNION_MEMBERS,".into()),
3656                    Line("MEMBERS_BY_DISCRIMINANT,".into()),
3657                    Line("MEMBERS_BY_NAME".into()),
3658                ]),
3659                Line(raw_schema_end),
3660            ]));
3661            if let Some(field_newtypes) = field_newtypes {
3662                private_mod_interior.push(field_newtypes);
3663            }
3664
3665            private_mod_interior.push(generate_members_by_discriminant(*node_reader)?);
3666            private_mod_interior.push(generate_members_by_name(*node_reader)?);
3667
3668            let mut has_pointer_field = false;
3669            let fields = struct_reader.get_fields()?;
3670            for field in fields {
3671                let name = get_field_name(field)?;
3672                let styled_name = camel_to_snake_case(name);
3673
3674                let discriminant_value = field.get_discriminant_value();
3675                let is_union_field = discriminant_value != field::NO_DISCRIMINANT;
3676
3677                match field.which()? {
3678                    field::Slot(s) => match s.get_type()?.which()? {
3679                        type_::Text(())
3680                        | type_::Data(())
3681                        | type_::List(_)
3682                        | type_::Struct(_)
3683                        | type_::Interface(_)
3684                        | type_::AnyPointer(_) => has_pointer_field = true,
3685                        _ => (),
3686                    },
3687                    field::Group(_) => has_pointer_field = true,
3688                }
3689
3690                if !is_union_field {
3691                    pipeline_impl_interior.push(generate_pipeline_getter(ctx, field)?);
3692                    let (ty, get, default_decl) = getter_text(ctx, &field, true, true)?;
3693                    if let Some(default) = default_decl {
3694                        private_mod_interior.push(default.clone());
3695                    }
3696                    reader_members.push(Branch(vec![
3697                        line("#[inline]"),
3698                        Line(format!("pub fn get_{styled_name}(self) {ty} {{")),
3699                        indent(get),
3700                        line("}"),
3701                    ]));
3702
3703                    let (ty_b, get_b, _) = getter_text(ctx, &field, false, true)?;
3704                    builder_members.push(Branch(vec![
3705                        line("#[inline]"),
3706                        Line(format!("pub fn get_{styled_name}(self) {ty_b} {{")),
3707                        indent(get_b),
3708                        line("}"),
3709                    ]));
3710                } else {
3711                    union_fields.push(field);
3712                }
3713
3714                builder_members.push(generate_setter(
3715                    ctx,
3716                    discriminant_offset,
3717                    &styled_name,
3718                    &field,
3719                )?);
3720
3721                reader_members.push(generate_haser(
3722                    discriminant_offset,
3723                    &styled_name,
3724                    &field,
3725                    true,
3726                )?);
3727                builder_members.push(generate_haser(
3728                    discriminant_offset,
3729                    &styled_name,
3730                    &field,
3731                    false,
3732                )?);
3733
3734                if let Ok(field::Group(group)) = field.which() {
3735                    let id = group.get_type_id();
3736                    let text = generate_node(ctx, id, ctx.get_last_name(id)?)?;
3737                    nested_output.push(text);
3738
3739                    // If this group is a stamped inline newtype, give its per-instance module the
3740                    // newtype's semantic trait.
3741                    let alias_id = field.get_type_id();
3742                    if alias_id != 0 {
3743                        nested_output.push(generate_newtype_impl(ctx, alias_id, id)?);
3744                    }
3745                }
3746            }
3747
3748            if discriminant_count > 0 {
3749                let (which_enums1, union_getter, typedef, mut default_decls) =
3750                    generate_union(ctx, discriminant_offset, &union_fields, true, &params)?;
3751                which_enums.push(which_enums1);
3752                which_enums.push(typedef);
3753                reader_members.push(union_getter);
3754
3755                private_mod_interior.append(&mut default_decls);
3756
3757                let (_, union_getter, typedef, _) =
3758                    generate_union(ctx, discriminant_offset, &union_fields, false, &params)?;
3759                which_enums.push(typedef);
3760                builder_members.push(union_getter);
3761
3762                let mut reexports = String::new();
3763                reexports.push_str("pub use self::Which::{");
3764                let mut whichs = Vec::new();
3765                for f in &union_fields {
3766                    whichs.push(capitalize_first_letter(get_field_name(*f)?));
3767                }
3768                reexports.push_str(&whichs.join(","));
3769                reexports.push_str("};");
3770                preamble.push(Line(reexports));
3771                preamble.push(BlankLine);
3772            }
3773
3774            let builder_struct_size =
3775                Branch(vec![
3776                    Line(fmt!(ctx,"impl <{0}> {capnp}::traits::HasStructSize for Builder<'_,{0}> {1} {{",
3777                                 params.params, params.where_clause)),
3778                                 indent(Line(
3779                        fmt!(ctx,"const STRUCT_SIZE: {capnp}::private::layout::StructSize = {capnp}::private::layout::StructSize {{ data: {}, pointers: {} }};", data_size as usize, pointer_size as usize))),
3780                   line("}")]);
3781
3782            private_mod_interior.push(Line(format!(
3783                "pub(crate) const TYPE_ID: u64 = {};",
3784                format_u64(node_id)
3785            )));
3786
3787            let from_pointer_builder_impl =
3788                Branch(vec![
3789                    Line(fmt!(ctx,"impl <'a,{0}> {capnp}::traits::FromPointerBuilder<'a> for Builder<'a,{0}> {1} {{", params.params, params.where_clause)),
3790                    indent(vec![
3791                        Line(fmt!(ctx,"fn init_pointer(builder: {capnp}::private::layout::PointerBuilder<'a>, _size: u32) -> Self {{")),
3792                        indent(Line(fmt!(ctx,"builder.init_struct(<Self as {capnp}::traits::HasStructSize>::STRUCT_SIZE).into()"))),
3793                        line("}"),
3794                        Line(fmt!(ctx,"fn get_from_pointer(builder: {capnp}::private::layout::PointerBuilder<'a>, default: ::core::option::Option<&'a [{capnp}::Word]>) -> {capnp}::Result<Self> {{")),
3795                        indent(Line(fmt!(ctx,"::core::result::Result::Ok(builder.get_struct(<Self as {capnp}::traits::HasStructSize>::STRUCT_SIZE, default)?.into())"))),
3796                        line("}")
3797                    ]),
3798                    line("}"),
3799                    BlankLine]);
3800
3801            let accessors = vec![
3802                Branch(preamble),
3803                (if !is_generic {
3804                    Branch(vec![
3805                        Line("#[derive(Copy, Clone)]".into()),
3806                        line("pub struct Owned(());"),
3807                        Line(fmt!(ctx,"impl {capnp}::introspect::Introspect for Owned {{ fn introspect() -> {capnp}::introspect::Type {{ {capnp}::introspect::TypeVariant::Struct({capnp}::introspect::RawBrandedStructSchema {{ generic: &_private::RAW_SCHEMA, field_types: _private::get_field_types, annotation_types: _private::get_annotation_types, type_id: ::core::any::TypeId::of::<Owned>() }}).into() }} }}")),
3808                        Line(fmt!(ctx, "impl {capnp}::traits::Owned for Owned {{ type Reader<'a> = Reader<'a>; type Builder<'a> = Builder<'a>; }}")),
3809                        Line(fmt!(ctx,"impl {capnp}::traits::OwnedStruct for Owned {{ type Reader<'a> = Reader<'a>; type Builder<'a> = Builder<'a>; }}")),
3810                        Line(fmt!(ctx,"impl {capnp}::traits::Pipelined for Owned {{ type Pipeline = Pipeline; }}"))
3811                    ])
3812                } else {
3813                    Branch(vec![
3814                        Line("#[derive(Copy, Clone)]".into()),
3815                        Line(format!("pub struct Owned<{}> {{", params.params)),
3816                            indent(Line(params.phantom_data_type.clone())),
3817                        line("}"),
3818                        Line(fmt!(ctx,"impl <{0}> {capnp}::introspect::Introspect for Owned <{0}> {1} {{ fn introspect() -> {capnp}::introspect::Type {{ {capnp}::introspect::TypeVariant::Struct({capnp}::introspect::RawBrandedStructSchema {{ generic: &_private::RAW_SCHEMA, field_types: _private::get_field_types::<{0}>, annotation_types: _private::get_annotation_types::<{0}>, type_id: ::core::any::TypeId::of::<Owned<{0}>>() }}).into() }} }}",
3819                            params.params, params.where_clause)),
3820                        Line(fmt!(ctx,"impl <{0}> {capnp}::traits::Owned for Owned <{0}> {1} {{ type Reader<'a> = Reader<'a, {0}>; type Builder<'a> = Builder<'a, {0}>; }}",
3821                            params.params, params.where_clause)),
3822                        Line(fmt!(ctx,"impl <{0}> {capnp}::traits::OwnedStruct for Owned <{0}> {1} {{ type Reader<'a> = Reader<'a, {0}>; type Builder<'a> = Builder<'a, {0}>; }}",
3823                            params.params, params.where_clause)),
3824                        Line(fmt!(ctx,"impl <{0}> {capnp}::traits::Pipelined for Owned<{0}> {1} {{ type Pipeline = Pipeline{2}; }}",
3825                            params.params, params.where_clause, bracketed_params)),
3826                    ])
3827                }),
3828                BlankLine,
3829                (if !is_generic {
3830                    Line(fmt!(ctx,"pub struct Reader<'a> {{ reader: {capnp}::private::layout::StructReader<'a> }}"))
3831                } else {
3832                    Branch(vec![
3833                        Line(format!("pub struct Reader<'a,{}> {} {{", params.params, params.where_clause)),
3834                        indent(vec![
3835                            Line(fmt!(ctx,"reader: {capnp}::private::layout::StructReader<'a>,")),
3836                            Line(params.phantom_data_type.clone()),
3837                        ]),
3838                        line("}")
3839                    ])
3840                }),
3841                // Manually implement Copy/Clone because `derive` only kicks in if all of
3842                // the parameters are known to implement Copy/Clone.
3843                Branch(vec![
3844                    Line(format!("impl <{0}> ::core::marker::Copy for Reader<'_,{0}> {1} {{}}",
3845                                 params.params, params.where_clause)),
3846                    Line(format!("impl <{0}> ::core::clone::Clone for Reader<'_,{0}> {1} {{",
3847                                 params.params, params.where_clause)),
3848                    indent(Line("fn clone(&self) -> Self { *self }".into())),
3849                    Line("}".into())]),
3850                BlankLine,
3851                Branch(vec![
3852                        Line(fmt!(ctx,"impl <{0}> {capnp}::traits::HasTypeId for Reader<'_,{0}> {1} {{",
3853                            params.params, params.where_clause)),
3854                        indent(vec![line("const TYPE_ID: u64 = _private::TYPE_ID;")]),
3855                    line("}")]),
3856                Line(fmt!(ctx,"impl <'a,{0}> ::core::convert::From<{capnp}::private::layout::StructReader<'a>> for Reader<'a,{0}> {1} {{",
3857                            params.params, params.where_clause)),
3858                indent(vec![
3859                    Line(fmt!(ctx,"fn from(reader: {capnp}::private::layout::StructReader<'a>) -> Self {{")),
3860                    indent(Line(format!("Self {{ reader, {} }}", params.phantom_data_value))),
3861                    line("}")
3862                ]),
3863                line("}"),
3864                BlankLine,
3865                Line(fmt!(ctx,"impl <'a,{0}> ::core::convert::From<Reader<'a,{0}>> for {capnp}::dynamic_value::Reader<'a> {1} {{",
3866                            params.params, params.where_clause)),
3867                indent(vec![
3868                    Line(format!("fn from(reader: Reader<'a,{0}>) -> Self {{", params.params)),
3869                    indent(Line(fmt!(ctx,"Self::Struct({capnp}::dynamic_struct::Reader::new(reader.reader, {capnp}::schema::StructSchema::new({capnp}::introspect::RawBrandedStructSchema {{ generic: &_private::RAW_SCHEMA, field_types: _private::get_field_types::<{0}>, annotation_types: _private::get_annotation_types::<{0}>, type_id: ::core::any::TypeId::of::<Owned<{0}>>()}})))", params.params))),
3870                    line("}")
3871                ]),
3872                line("}"),
3873                BlankLine,
3874                Line(format!("impl <{0}> ::core::fmt::Debug for Reader<'_,{0}> {1} {{",
3875                            params.params, params.where_clause)),
3876                indent(vec![
3877                    Line("fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::result::Result<(), ::core::fmt::Error> {".into()),
3878                    indent(Line(fmt!(ctx,"core::fmt::Debug::fmt(&::core::convert::Into::<{capnp}::dynamic_value::Reader<'_>>::into(*self), f)"))),
3879                    line("}")
3880                ]),
3881                line("}"),
3882
3883                BlankLine,
3884
3885                Line(fmt!(ctx,"impl <'a,{0}> {capnp}::traits::FromPointerReader<'a> for Reader<'a,{0}> {1} {{",
3886                    params.params, params.where_clause)),
3887                indent(vec![
3888                    Line(fmt!(ctx,"fn get_from_pointer(reader: &{capnp}::private::layout::PointerReader<'a>, default: ::core::option::Option<&'a [{capnp}::Word]>) -> {capnp}::Result<Self> {{")),
3889                    indent(line("::core::result::Result::Ok(reader.get_struct(default)?.into())")),
3890                    line("}")
3891                ]),
3892                line("}"),
3893                BlankLine,
3894                Line(fmt!(ctx,"impl <'a,{0}> {capnp}::traits::IntoInternalStructReader<'a> for Reader<'a,{0}> {1} {{",
3895                            params.params, params.where_clause)),
3896                indent(vec![
3897                    Line(fmt!(ctx,"fn into_internal_struct_reader(self) -> {capnp}::private::layout::StructReader<'a> {{")),
3898                    indent(line("self.reader")),
3899                    line("}")
3900                ]),
3901                line("}"),
3902                BlankLine,
3903                Line(fmt!(ctx,"impl <'a,{0}> {capnp}::traits::Imbue<'a> for Reader<'a,{0}> {1} {{",
3904                    params.params, params.where_clause)),
3905                indent(vec![
3906                    Line(fmt!(ctx,"fn imbue(&mut self, cap_table: &'a {capnp}::private::layout::CapTable) {{")),
3907                    indent(Line(fmt!(ctx,"self.reader.imbue({capnp}::private::layout::CapTableReader::from_ref(cap_table))"))),
3908                    line("}")
3909                ]),
3910                line("}"),
3911                BlankLine,
3912                if has_pointer_field { // we do this to keep clippy happy
3913                    Line(format!("impl <'a,{0}> Reader<'a,{0}> {1} {{", params.params, params.where_clause))
3914                } else {
3915                    Line(format!("impl <{0}> Reader<'_,{0}> {1} {{", params.params, params.where_clause))
3916                },
3917                indent(vec![
3918                        Line(format!("pub fn reborrow(&self) -> Reader<'_,{}> {{",params.params)),
3919                        indent(line("Self { .. *self }")),
3920                        line("}"),
3921                        BlankLine,
3922                        Line(fmt!(ctx,"pub fn total_size(&self) -> {capnp}::Result<{capnp}::MessageSize> {{")),
3923                        indent(line("self.reader.total_size()")),
3924                        line("}")]),
3925                indent(reader_members),
3926                line("}"),
3927                BlankLine,
3928                (if !is_generic {
3929                    Line(fmt!(ctx,"pub struct Builder<'a> {{ builder: {capnp}::private::layout::StructBuilder<'a> }}"))
3930                } else {
3931                    Branch(vec![
3932                        Line(format!("pub struct Builder<'a,{}> {} {{",
3933                                     params.params, params.where_clause)),
3934                            indent(vec![
3935                            Line(fmt!(ctx, "builder: {capnp}::private::layout::StructBuilder<'a>,")),
3936                            Line(params.phantom_data_type.clone()),
3937                        ]),
3938                        line("}")
3939                    ])
3940                }),
3941                builder_struct_size,
3942                Branch(vec![
3943                    Line(fmt!(ctx,"impl <{0}> {capnp}::traits::HasTypeId for Builder<'_,{0}> {1} {{",
3944                                 params.params, params.where_clause)),
3945                    indent(vec![
3946                        line("const TYPE_ID: u64 = _private::TYPE_ID;")]),
3947                    line("}")
3948                ]),
3949                Line(fmt!(ctx,
3950                    "impl <'a,{0}> ::core::convert::From<{capnp}::private::layout::StructBuilder<'a>> for Builder<'a,{0}> {1} {{",
3951                    params.params, params.where_clause)),
3952                indent(vec![
3953                        Line(fmt!(ctx,"fn from(builder: {capnp}::private::layout::StructBuilder<'a>) -> Self {{")),
3954                        indent(Line(format!("Self {{ builder, {} }}", params.phantom_data_value))),
3955                        line("}")
3956                ]),
3957                line("}"),
3958                BlankLine,
3959                Line(fmt!(ctx,"impl <'a,{0}> {capnp}::traits::IntoInternalStructBuilder<'a> for Builder<'a,{0}> {1} {{",
3960                            params.params, params.where_clause)),
3961                indent(vec![
3962                    Line(fmt!(ctx,"fn into_internal_struct_builder(self) -> {capnp}::private::layout::StructBuilder<'a> {{")),
3963                    indent(line("self.builder")),
3964                    line("}")
3965                ]),
3966                line("}"),
3967                BlankLine,
3968                Line(fmt!(ctx,"impl <'a,{0}> ::core::convert::From<Builder<'a,{0}>> for {capnp}::dynamic_value::Builder<'a> {1} {{",
3969                            params.params, params.where_clause)),
3970                indent(vec![
3971                        Line(format!("fn from(builder: Builder<'a,{0}>) -> Self {{", params.params)),
3972                        indent(Line(fmt!(ctx,"Self::Struct({capnp}::dynamic_struct::Builder::new(builder.builder, {capnp}::schema::StructSchema::new({capnp}::introspect::RawBrandedStructSchema {{ generic: &_private::RAW_SCHEMA, field_types: _private::get_field_types::<{0}>, annotation_types: _private::get_annotation_types::<{0}>, type_id: ::core::any::TypeId::of::<Owned<{0}>>()}})))", params.params))),
3973                        line("}")
3974                ]),
3975                line("}"),
3976                BlankLine,
3977
3978                Line(fmt!(ctx,"impl <'a,{0}> {capnp}::traits::ImbueMut<'a> for Builder<'a,{0}> {1} {{",
3979                             params.params, params.where_clause)),
3980                indent(vec![
3981                        Line(fmt!(ctx,"fn imbue_mut(&mut self, cap_table: &'a mut {capnp}::private::layout::CapTable) {{")),
3982                        indent(Line(fmt!(ctx,"self.builder.imbue({capnp}::private::layout::CapTableBuilder::from_ref(cap_table))"))),
3983                        line("}")]),
3984                line("}"),
3985                BlankLine,
3986
3987                from_pointer_builder_impl,
3988                Line(fmt!(ctx,
3989                    "impl <{0}> {capnp}::traits::SetterInput<Owned<{0}>> for Reader<'_,{0}> {1} {{",
3990                    params.params, params.where_clause)),
3991                indent(Line(fmt!(ctx,"fn set_pointer_builder(mut pointer: {capnp}::private::layout::PointerBuilder<'_>, value: Self, canonicalize: bool) -> {capnp}::Result<()> {{ pointer.set_struct(&value.reader, canonicalize) }}"))),
3992                line("}"),
3993                BlankLine,
3994                Line(format!("impl <'a,{0}> Builder<'a,{0}> {1} {{", params.params, params.where_clause)),
3995                indent(vec![
3996                        Line(format!("pub fn into_reader(self) -> Reader<'a,{}> {{", params.params)),
3997                        indent(line("self.builder.into_reader().into()")),
3998                        line("}"),
3999                        Line(format!("pub fn reborrow(&mut self) -> Builder<'_,{}> {{", params.params)),
4000                        (if !is_generic {
4001                            indent(line("Builder { builder: self.builder.reborrow() }"))
4002                        } else {
4003                            indent(line("Builder { builder: self.builder.reborrow(), ..*self }"))
4004                        }),
4005                        line("}"),
4006                        Line(format!("pub fn reborrow_as_reader(&self) -> Reader<'_,{}> {{", params.params)),
4007                        indent(line("self.builder.as_reader().into()")),
4008                        line("}"),
4009
4010                        BlankLine,
4011                        Line(fmt!(ctx,"pub fn total_size(&self) -> {capnp}::Result<{capnp}::MessageSize> {{")),
4012                        indent(line("self.builder.as_reader().total_size()")),
4013                        line("}")
4014                        ]),
4015                indent(builder_members),
4016                line("}"),
4017                BlankLine,
4018                (if is_generic {
4019                    Branch(vec![
4020                        Line(format!("pub struct Pipeline{bracketed_params} {{")),
4021                        indent(vec![
4022                            Line(fmt!(ctx,"_typeless: {capnp}::any_pointer::Pipeline,")),
4023                            Line(params.phantom_data_type),
4024                        ]),
4025                        line("}")
4026                    ])
4027                } else {
4028                    Line(fmt!(ctx,"pub struct Pipeline {{ _typeless: {capnp}::any_pointer::Pipeline }}"))
4029                }),
4030                Line(fmt!(ctx,"impl{bracketed_params} {capnp}::capability::FromTypelessPipeline for Pipeline{bracketed_params} {{")),
4031                indent(vec![
4032                        Line(fmt!(ctx,"fn new(typeless: {capnp}::any_pointer::Pipeline) -> Self {{")),
4033                        indent(Line(format!("Self {{ _typeless: typeless, {} }}", params.phantom_data_value))),
4034                        line("}")]),
4035                line("}"),
4036                Line(format!("impl{0} Pipeline{0} {1} {{", bracketed_params,
4037                             params.pipeline_where_clause)),
4038                indent(pipeline_impl_interior),
4039                line("}"),
4040                line("mod _private {"),
4041                indent(private_mod_interior),
4042                line("}"),
4043            ];
4044
4045            output.push(indent(vec![
4046                Branch(accessors),
4047                Branch(which_enums),
4048                Branch(nested_output),
4049            ]));
4050            output.push(line("}"));
4051        }
4052
4053        node::Enum(enum_reader) => {
4054            let last_name = ctx.get_last_name(node_id)?;
4055            let name_as_mod = module_name(last_name);
4056            output.push(BlankLine);
4057
4058            let mut members = Vec::new();
4059            let mut match_branches = Vec::new();
4060            let enumerants = enum_reader.get_enumerants()?;
4061            for (ii, enumerant) in enumerants.into_iter().enumerate() {
4062                let enumerant = capitalize_first_letter(get_enumerant_name(enumerant)?);
4063                members.push(Line(format!("{enumerant} = {ii},")));
4064                match_branches.push(Line(format!(
4065                    "{ii} => ::core::result::Result::Ok(Self::{enumerant}),"
4066                )));
4067            }
4068            match_branches.push(Line(fmt!(
4069                ctx,
4070                "n => ::core::result::Result::Err({capnp}::NotInSchema(n)),"
4071            )));
4072
4073            output.push(Branch(vec![
4074                line("#[repr(u16)]"),
4075                line("#[derive(Clone, Copy, Debug, PartialEq, Eq)]"),
4076                Line(format!("pub enum {last_name} {{")),
4077                indent(members),
4078                line("}"),
4079            ]));
4080
4081            output.push(BlankLine);
4082            output.push(Branch(vec![
4083                Line(fmt!(ctx,
4084                    "impl {capnp}::introspect::Introspect for {last_name} {{"
4085                )),
4086                indent(Line(fmt!(ctx,
4087                    "fn introspect() -> {capnp}::introspect::Type {{ {capnp}::introspect::TypeVariant::Enum({capnp}::introspect::RawEnumSchema::new(&{0}::ARENA, {0}::get_annotation_types)).into() }}", name_as_mod))),
4088                Line("}".into()),
4089            ]));
4090
4091            output.push(Branch(vec![
4092                Line(fmt!(ctx,"impl ::core::convert::From<{last_name}> for {capnp}::dynamic_value::Reader<'_> {{")),
4093                indent(Line(fmt!(ctx,
4094                    "fn from(e: {last_name}) -> Self {{ {capnp}::dynamic_value::Enum::new(e.into(), {capnp}::introspect::RawEnumSchema::new(&{0}::ARENA, {0}::get_annotation_types).into()).into() }}", name_as_mod ))),
4095                Line("}".into())
4096            ]));
4097
4098            output.push(Branch(vec![
4099                Line(format!(
4100                    "impl ::core::convert::TryFrom<u16> for {last_name} {{"
4101                )),
4102                indent(Line(
4103                    fmt!(ctx,"type Error = {capnp}::NotInSchema;"),
4104                )),
4105                indent(vec![
4106                    Line(
4107                        format!("fn try_from(value: u16) -> ::core::result::Result<Self, <{last_name} as ::core::convert::TryFrom<u16>>::Error> {{")
4108                    ),
4109                    indent(vec![
4110                        line("match value {"),
4111                        indent(match_branches),
4112                        line("}"),
4113                    ]),
4114                    line("}"),
4115                ]),
4116                line("}"),
4117                Line(format!("impl From<{last_name}> for u16 {{")),
4118                indent(line("#[inline]")),
4119                indent(Line(format!(
4120                    "fn from(x: {last_name}) -> u16 {{ x as u16 }}"
4121                ))),
4122                line("}"),
4123            ]));
4124
4125            output.push(Branch(vec![
4126                Line(fmt!(
4127                    ctx,
4128                    "impl {capnp}::traits::HasTypeId for {last_name} {{"
4129                )),
4130                indent(Line(format!(
4131                    "const TYPE_ID: u64 = {}u64;",
4132                    format_u64(node_id)
4133                ))),
4134                line("}"),
4135            ]));
4136
4137            output.push(Branch(vec![
4138                Line(format!("mod {name_as_mod} {{")),
4139                Branch(vec![
4140                    crate::pointer_constants::node_word_array_declaration(
4141                        ctx,
4142                        "ENCODED_NODE",
4143                        *node_reader,
4144                        crate::pointer_constants::WordArrayDeclarationOptions { pub_crate: true },
4145                    )?,
4146                    generate_get_annotation_types(ctx, *node_reader)?,
4147                    Line(fmt!(ctx, "pub(crate) static ARENA: {capnp}::private::arena::GeneratedCodeArena = {capnp}::private::arena::GeneratedCodeArena::new(&ENCODED_NODE);")),
4148                ]),
4149                Line("}".into()),
4150            ]));
4151        }
4152
4153        node::Interface(interface) => {
4154            let params = node_reader.parameters_texts(ctx);
4155            output.push(BlankLine);
4156
4157            let is_generic = node_reader.get_is_generic();
4158
4159            let names = &ctx.scope_map[&node_id];
4160            let mut client_impl_interior = Vec::new();
4161            let mut server_interior = Vec::new();
4162            let mut mod_interior = Vec::new();
4163            let mut dispatch_arms = Vec::new();
4164            let mut private_mod_interior = Vec::new();
4165
4166            let bracketed_params = if params.params.is_empty() {
4167                "".to_string()
4168            } else {
4169                format!("<{}>", params.params)
4170            };
4171
4172            private_mod_interior.push(Line(format!(
4173                "pub(crate) const TYPE_ID: u64 = {};",
4174                format_u64(node_id)
4175            )));
4176
4177            mod_interior.push(line("#![allow(unused_variables)]"));
4178
4179            let methods = interface.get_methods()?;
4180            for (ordinal, method) in methods.into_iter().enumerate() {
4181                let name = method.get_name()?.to_str()?;
4182
4183                let param_id = method.get_param_struct_type();
4184                let param_node = &ctx.node_map[&param_id];
4185                let (param_scopes, params_ty_params) = if param_node.get_scope_id() == 0 {
4186                    let mut names = names.clone();
4187                    let local_name = module_name(&format!("{name}Params"));
4188                    nested_output.push(generate_node(ctx, param_id, &local_name)?);
4189                    names.push(local_name);
4190                    (names, params.params.clone())
4191                } else {
4192                    (
4193                        ctx.scope_map[&param_node.get_id()].clone(),
4194                        get_ty_params_of_brand(ctx, method.get_param_brand()?)?,
4195                    )
4196                };
4197                let param_type = do_branding(
4198                    ctx,
4199                    param_id,
4200                    method.get_param_brand()?,
4201                    Leaf::Owned,
4202                    &param_scopes.join("::"),
4203                )?;
4204
4205                mod_interior.push(Line(fmt!(
4206                    ctx,
4207                    "pub type {}Params<{}> = {capnp}::capability::Params<{}>;",
4208                    capitalize_first_letter(name),
4209                    params_ty_params,
4210                    param_type
4211                )));
4212
4213                let result_id = method.get_result_struct_type();
4214                if result_id != STREAM_RESULT_ID {
4215                    dispatch_arms.push(
4216                        Line(fmt!(ctx,
4217                                  "{ordinal} => {capnp}::capability::DispatchCallResult::new({capnp}::capability::Promise::from_future(<_T as Server{bracketed_params}>::{}(this, {capnp}::private::capability::internal_get_typed_params(params), {capnp}::private::capability::internal_get_typed_results(results))), false),",
4218                                  module_name(name))));
4219
4220                    let result_node = &ctx.node_map[&result_id];
4221                    let (result_scopes, results_ty_params) = if result_node.get_scope_id() == 0 {
4222                        let mut names = names.clone();
4223                        let local_name = module_name(&format!("{name}Results"));
4224                        nested_output.push(generate_node(ctx, result_id, &local_name)?);
4225                        names.push(local_name);
4226                        (names, params.params.clone())
4227                    } else {
4228                        (
4229                            ctx.scope_map[&result_node.get_id()].clone(),
4230                            get_ty_params_of_brand(ctx, method.get_result_brand()?)?,
4231                        )
4232                    };
4233                    let result_type = do_branding(
4234                        ctx,
4235                        result_id,
4236                        method.get_result_brand()?,
4237                        Leaf::Owned,
4238                        &result_scopes.join("::"),
4239                    )?;
4240                    mod_interior.push(Line(fmt!(
4241                        ctx,
4242                        "pub type {}Results<{}> = {capnp}::capability::Results<{}>;",
4243                        capitalize_first_letter(name),
4244                        results_ty_params,
4245                        result_type
4246                    )));
4247                    server_interior.push(
4248                        Line(fmt!(ctx,
4249                                  "fn {}(self: {capnp}::capability::Rc<Self>, _: {}Params<{}>, _: {}Results<{}>) -> impl ::core::future::Future<Output = Result<(), {capnp}::Error>> + 'static {{ ::core::future::ready(Err({capnp}::Error::unimplemented(\"method {}::Server::{} not implemented\".to_string()))) }}",
4250                                  module_name(name),
4251                                  capitalize_first_letter(name), params_ty_params,
4252                                  capitalize_first_letter(name), results_ty_params,
4253                                  node_name, module_name(name)
4254                        )));
4255
4256                    client_impl_interior.push(Line(fmt!(
4257                        ctx,
4258                        "pub fn {}_request(&self) -> {capnp}::capability::Request<{},{}> {{",
4259                        camel_to_snake_case(name),
4260                        param_type,
4261                        result_type
4262                    )));
4263
4264                    client_impl_interior.push(indent(Line(format!(
4265                        "self.client.new_call(_private::TYPE_ID, {ordinal}, ::core::option::Option::None)"
4266                    ))));
4267                    client_impl_interior.push(line("}"));
4268                } else {
4269                    // It's a streaming method.
4270                    dispatch_arms.push(
4271                        Line(fmt!(ctx,
4272                                  "{ordinal} => {capnp}::capability::DispatchCallResult::new({capnp}::capability::Promise::from_future(<_T as Server{bracketed_params}>::{}(this, {capnp}::private::capability::internal_get_typed_params(params))), true),",
4273
4274                                  module_name(name))));
4275
4276                    server_interior.push(
4277                        Line(fmt!(ctx,
4278                                  "fn {}(self: {capnp}::capability::Rc<Self>, _: {}Params<{}>) -> impl ::core::future::Future<Output = Result<(), {capnp}::Error>> + 'static {{ ::core::future::ready(Err({capnp}::Error::unimplemented(\"method {}::Server::{} not implemented\".to_string()))) }}",
4279                                  module_name(name),
4280                                  capitalize_first_letter(name), params_ty_params,
4281                                  node_name, module_name(name)
4282                        )));
4283                    client_impl_interior.push(Line(fmt!(
4284                        ctx,
4285                        "pub fn {}_request(&self) -> {capnp}::capability::StreamingRequest<{}> {{",
4286                        camel_to_snake_case(name),
4287                        param_type
4288                    )));
4289                    client_impl_interior.push(indent(Line(format!(
4290                        "self.client.new_streaming_call(_private::TYPE_ID, {ordinal}, ::core::option::Option::None)"
4291                    ))));
4292
4293                    client_impl_interior.push(line("}"));
4294                }
4295
4296                method.get_annotations()?;
4297            }
4298
4299            let mut base_dispatch_arms = Vec::new();
4300
4301            let server_base = {
4302                let mut base_traits = Vec::new();
4303
4304                fn find_super_interfaces<'a>(
4305                    interface: schema_capnp::node::interface::Reader<'a>,
4306                    all_extends: &mut Vec<
4307                        <schema_capnp::superclass::Owned as capnp::traits::OwnedStruct>::Reader<'a>,
4308                    >,
4309                    ctx: &GeneratorContext<'a>,
4310                ) -> ::capnp::Result<()> {
4311                    let extends = interface.get_superclasses()?;
4312                    for superclass in extends {
4313                        if let node::Interface(interface) =
4314                            ctx.node_map[&superclass.get_id()].which()?
4315                        {
4316                            find_super_interfaces(interface, all_extends, ctx)?;
4317                        }
4318                        all_extends.push(superclass);
4319                    }
4320                    Ok(())
4321                }
4322
4323                let mut extends = Vec::new();
4324                find_super_interfaces(interface, &mut extends, ctx)?;
4325                for interface in &extends {
4326                    let type_id = interface.get_id();
4327                    let brand = interface.get_brand()?;
4328                    let the_mod = ctx.get_qualified_module(type_id);
4329
4330                    base_dispatch_arms.push(Line(format!(
4331                        "0x{type_id:x} => {}::dispatch_call_internal(self.server, method_id, params, results),",
4332                        do_branding(
4333                            ctx, type_id, brand, Leaf::ServerDispatch, &the_mod)?)));
4334                    base_traits.push(do_branding(ctx, type_id, brand, Leaf::Server, &the_mod)?);
4335                }
4336
4337                // Defining that the server itself should always be 'static makes
4338                // bounds easier down the line.
4339                if !extends.is_empty() {
4340                    format!(": {} + 'static", base_traits.join(" + "))
4341                } else {
4342                    ": 'static".to_string()
4343                }
4344            };
4345
4346            mod_interior.push(BlankLine);
4347            mod_interior.push(Line(format!("pub struct Client{bracketed_params} {{")));
4348            mod_interior.push(indent(Line(fmt!(
4349                ctx,
4350                "pub client: {capnp}::capability::Client,"
4351            ))));
4352            if is_generic {
4353                mod_interior.push(indent(Line(params.phantom_data_type.clone())));
4354            }
4355            mod_interior.push(line("}"));
4356            mod_interior.push(
4357                Branch(vec![
4358                    Line(fmt!(ctx,"impl {bracketed_params} {capnp}::capability::FromClientHook for Client{bracketed_params} {{")),
4359                    indent(Line(fmt!(ctx,"fn new(hook: Box<{capnp}::capability::DynClientHook>) -> Self {{"))),
4360                    indent(indent(Line(fmt!(ctx,"Self {{ client: {capnp}::capability::Client::new(hook), {} }}", params.phantom_data_value)))),
4361                    indent(line("}")),
4362                    indent(Line(fmt!(ctx,"fn into_client_hook(self) -> Box<{capnp}::capability::DynClientHook> {{"))),
4363                    indent(indent(line("self.client.hook"))),
4364                    indent(line("}")),
4365                    indent(Line(fmt!(ctx,"fn as_client_hook(&self) -> &{capnp}::capability::DynClientHook {{"))),
4366                    indent(indent(line("&*self.client.hook"))),
4367                    indent(line("}")),
4368                    line("}")]));
4369
4370            mod_interior.push(if !is_generic {
4371                Branch(vec![
4372                    Line("#[derive(Copy, Clone)]".into()),
4373                    line("pub struct Owned(());"),
4374                    Line(fmt!(ctx,"impl {capnp}::introspect::Introspect for Owned {{ fn introspect() -> {capnp}::introspect::Type {{ {capnp}::introspect::TypeVariant::Capability.into() }} }}")),
4375                    Line(fmt!(ctx,"impl {capnp}::traits::Owned for Owned {{ type Reader<'a> = Client; type Builder<'a> = Client; }}")),
4376                    Line(fmt!(ctx,"impl {capnp}::traits::Pipelined for Owned {{ type Pipeline = Client; }}"))])
4377            } else {
4378                Branch(vec![
4379                    Line("#[derive(Copy, Clone)]".into()),
4380                    Line(format!("pub struct Owned<{}> {} {{", params.params, params.where_clause)),
4381                    indent(Line(params.phantom_data_type.clone())),
4382                    line("}"),
4383                    Line(fmt!(ctx,
4384                              "impl <{0}> {capnp}::introspect::Introspect for Owned <{0}> {1} {{ fn introspect() -> {capnp}::introspect::Type {{ {capnp}::introspect::TypeVariant::Capability.into() }} }}",
4385                              params.params, params.where_clause)),
4386                    Line(fmt!(ctx,
4387                        "impl <{0}> {capnp}::traits::Owned for Owned <{0}> {1} {{ type Reader<'a> = Client<{0}>; type Builder<'a> = Client<{0}>; }}",
4388                        params.params, params.where_clause)),
4389                    Line(fmt!(ctx,
4390                        "impl <{0}> {capnp}::traits::Pipelined for Owned <{0}> {1} {{ type Pipeline = Client{2}; }}",
4391                        params.params, params.where_clause, bracketed_params))])
4392            });
4393
4394            mod_interior.push(Branch(vec![
4395                Line(fmt!(ctx,"impl <'a,{0}> {capnp}::traits::FromPointerReader<'a> for Client<{0}> {1} {{",
4396                    params.params, params.where_clause)),
4397                indent(vec![
4398                        Line(fmt!(ctx,"fn get_from_pointer(reader: &{capnp}::private::layout::PointerReader<'a>, _default: ::core::option::Option<&'a [{capnp}::Word]>) -> {capnp}::Result<Self> {{")),
4399                        indent(Line(fmt!(ctx,"::core::result::Result::Ok({capnp}::capability::FromClientHook::new(reader.get_capability()?))"))),
4400                        line("}")]),
4401                line("}")]));
4402
4403            mod_interior.push(Branch(vec![
4404                Line(fmt!(ctx,"impl <'a,{0}> {capnp}::traits::FromPointerBuilder<'a> for Client<{0}> {1} {{",
4405                             params.params, params.where_clause)),
4406                indent(vec![
4407                            Line(fmt!(ctx,"fn init_pointer(_builder: {capnp}::private::layout::PointerBuilder<'a>, _size: u32) -> Self {{")),
4408                            indent(line("unimplemented!()")),
4409                            line("}"),
4410                            Line(fmt!(ctx,"fn get_from_pointer(builder: {capnp}::private::layout::PointerBuilder<'a>, _default: ::core::option::Option<&'a [{capnp}::Word]>) -> {capnp}::Result<Self> {{")),
4411                            indent(Line(fmt!(ctx,"::core::result::Result::Ok({capnp}::capability::FromClientHook::new(builder.get_capability()?))"))),
4412                            line("}")]),
4413                line("}"),
4414                BlankLine]));
4415
4416            mod_interior.push(Branch(vec![
4417                Line(fmt!(ctx,
4418                    "impl <{0}> {capnp}::traits::SetterInput<Owned<{0}>> for Client<{0}> {1} {{",
4419                    params.params, params.where_clause)),
4420                indent(vec![
4421                            Line(fmt!(ctx,"fn set_pointer_builder(mut pointer: {capnp}::private::layout::PointerBuilder<'_>, from: Self, _canonicalize: bool) -> {capnp}::Result<()> {{")),
4422                            indent(Line("pointer.set_capability(from.client.hook);".to_string())),
4423                            indent(Line("::core::result::Result::Ok(())".to_string())),
4424                            line("}")
4425                        ]
4426                ),
4427                line("}")]));
4428
4429            mod_interior.push(Branch(vec![
4430                Line(fmt!(ctx,
4431                    "impl {bracketed_params} {capnp}::traits::HasTypeId for Client{bracketed_params} {{"
4432                )),
4433                indent(Line(
4434                    "const TYPE_ID: u64 = _private::TYPE_ID;".to_string(),
4435                )),
4436                line("}"),
4437            ]));
4438
4439            mod_interior.push(Branch(vec![
4440                Line(format!(
4441                    "impl {bracketed_params} Clone for Client{bracketed_params} {{"
4442                )),
4443                indent(line("fn clone(&self) -> Self {")),
4444                indent(indent(Line(format!(
4445                    "Self {{ client: self.client.clone(), {} }}",
4446                    params.phantom_data_value
4447                )))),
4448                indent(line("}")),
4449                line("}"),
4450            ]));
4451
4452            mod_interior.push(Branch(vec![
4453                Line(format!(
4454                    "impl {bracketed_params} Client{bracketed_params} {{"
4455                )),
4456                indent(client_impl_interior),
4457                line("}"),
4458            ]));
4459
4460            mod_interior.push(Branch(vec![
4461                Line(format!(
4462                    "pub trait Server<{}> {} {} {{",
4463                    params.params, server_base, params.where_clause
4464                )),
4465                indent(server_interior),
4466                line("}"),
4467            ]));
4468
4469            mod_interior.push(Branch(vec![
4470                Line(format!(
4471                    "pub struct ServerDispatch<_T,{}> {{",
4472                    params.params
4473                )),
4474                indent(line(fmt!(ctx, "pub server: {capnp}::capability::Rc<_T>,"))),
4475                indent(if is_generic {
4476                    vec![Line(params.phantom_data_type.clone())]
4477                } else {
4478                    vec![]
4479                }),
4480                line("}"),
4481            ]));
4482
4483            mod_interior.push(Branch(vec![
4484                Line(
4485                    fmt!(ctx,"impl <_S: Server{1} + 'static, {0}> {capnp}::capability::FromServer<_S> for Client{1} {2}  {{",
4486                            params.params, bracketed_params, params.where_clause)),
4487                indent(vec![
4488                    Line(format!("type Dispatch = ServerDispatch<_S, {}>;", params.params)),
4489                    Line(fmt!(ctx, "fn from_server(s: {capnp}::capability::Rc<_S>) -> ServerDispatch<_S, {}> {{", params.params)),
4490                    indent(Line(format!("ServerDispatch {{ server: s, {} }}", params.phantom_data_value))),
4491                    line("}"),
4492                ]),
4493                line("}"),
4494            ]));
4495
4496            mod_interior.push(
4497                Branch(vec![
4498                    (if is_generic {
4499                        Line(format!("impl <{}, _T: Server{}> ::core::ops::Deref for ServerDispatch<_T,{}> {} {{", params.params, bracketed_params, params.params, params.where_clause))
4500                    } else {
4501                        line("impl <_T: Server> ::core::ops::Deref for ServerDispatch<_T> {")
4502                    }),
4503                    indent(line("type Target = _T;")),
4504                    indent(line("fn deref(&self) -> &_T { &self.server}")),
4505                    line("}"),
4506                    ]));
4507
4508            mod_interior.push(
4509                Branch(vec![
4510                    (if is_generic {
4511                        Line(format!("impl <{}, _T: Server{}> ::core::clone::Clone for ServerDispatch<_T,{}> {} {{", params.params, bracketed_params, params.params, params.where_clause))
4512                    } else {
4513                        line("impl <_T: Server> ::core::clone::Clone for ServerDispatch<_T> {")
4514                    }),
4515                    indent(line(
4516                        format!("fn clone(&self) -> Self {{ Self {{ server: self.server.clone(), {} }} }}", params.phantom_data_value))),
4517                    line("}"),
4518                    ]));
4519
4520            mod_interior.push(
4521                Branch(vec![
4522                    (if is_generic {
4523                        Line(fmt!(ctx,"impl <{}, _T: Server{}> {capnp}::capability::Server for ServerDispatch<_T,{}> {} {{", params.params, bracketed_params, params.params, params.where_clause))
4524                    } else {
4525                        Line(fmt!(ctx,"impl <_T: Server> {capnp}::capability::Server for ServerDispatch<_T> {{"))
4526                    }),
4527                    indent(Line(fmt!(ctx,"fn dispatch_call(self, interface_id: u64, method_id: u16, params: {capnp}::capability::Params<{capnp}::any_pointer::Owned>, results: {capnp}::capability::Results<{capnp}::any_pointer::Owned>) -> {capnp}::capability::DispatchCallResult {{"))),
4528                    indent(indent(line("match interface_id {"))),
4529                    indent(indent(indent(line("_private::TYPE_ID => Self::dispatch_call_internal(self.server, method_id, params, results),")))),
4530                    indent(indent(indent(base_dispatch_arms))),
4531                    indent(indent(indent(Line(fmt!(ctx,"_ => {{ {capnp}::capability::DispatchCallResult::new({capnp}::capability::Promise::err({capnp}::Error::unimplemented(\"Method not implemented.\".to_string())), false) }}"))))),
4532                    indent(indent(line("}"))),
4533                    indent(line("}")),
4534
4535                    indent(Line(fmt!(ctx, "fn as_ptr(&self) -> usize {{ {capnp}::capability::Rc::as_ptr(&self.server) as usize }}"))),
4536
4537                    line("}")]));
4538
4539            mod_interior.push(
4540                Branch(vec![
4541                    (if is_generic {
4542                        Line(format!("impl <{}, _T: Server{}> ServerDispatch<_T,{}> {} {{", params.params, bracketed_params, params.params, params.where_clause))
4543                    } else {
4544                        line("impl <_T :Server> ServerDispatch<_T> {")
4545                    }),
4546
4547                    indent(Line(fmt!(ctx,"pub fn dispatch_call_internal(this: {capnp}::capability::Rc<_T>, method_id: u16, params: {capnp}::capability::Params<{capnp}::any_pointer::Owned>, results: {capnp}::capability::Results<{capnp}::any_pointer::Owned>) -> {capnp}::capability::DispatchCallResult {{"))),
4548                    indent(indent(line("match method_id {"))),
4549                    indent(indent(indent(dispatch_arms))),
4550                    indent(indent(indent(Line(fmt!(ctx,"_ => {{ {capnp}::capability::DispatchCallResult::new({capnp}::capability::Promise::err({capnp}::Error::unimplemented(\"Method not implemented.\".to_string())), false) }}"))))),
4551                    indent(indent(line("}"))),
4552                    indent(line("}")),
4553                    line("}")]));
4554
4555            mod_interior.push(Branch(vec![
4556                line("pub(crate) mod _private {"),
4557                indent(private_mod_interior),
4558                line("}"),
4559            ]));
4560
4561            mod_interior.push(Branch(vec![Branch(nested_output)]));
4562
4563            output.push(BlankLine);
4564            if is_generic {
4565                output.push(Line(format!(
4566                    "pub mod {} {{ /* ({}) */",
4567                    node_name,
4568                    params.expanded_list.join(",")
4569                )));
4570            } else {
4571                output.push(Line(format!("pub mod {node_name} {{")));
4572            }
4573            output.push(indent(mod_interior));
4574            output.push(line("}"));
4575        }
4576
4577        node::Const(c) => {
4578            let styled_name = ctx.get_last_name(node_id)?.to_ascii_uppercase();
4579
4580            let typ = c.get_type()?;
4581            let formatted_text = match (typ.which()?, c.get_value()?.which()?) {
4582                (type_::Void(()), value::Void(())) => {
4583                    Line(format!("pub const {styled_name}: () = ();"))
4584                }
4585                (type_::Bool(()), value::Bool(b)) => {
4586                    Line(format!("pub const {styled_name}: bool = {b};"))
4587                }
4588                (type_::Int8(()), value::Int8(i)) => {
4589                    Line(format!("pub const {styled_name}: i8 = {i};"))
4590                }
4591                (type_::Int16(()), value::Int16(i)) => {
4592                    Line(format!("pub const {styled_name}: i16 = {i};"))
4593                }
4594                (type_::Int32(()), value::Int32(i)) => {
4595                    Line(format!("pub const {styled_name}: i32 = {i};"))
4596                }
4597                (type_::Int64(()), value::Int64(i)) => {
4598                    Line(format!("pub const {styled_name}: i64 = {i};"))
4599                }
4600                (type_::Uint8(()), value::Uint8(i)) => {
4601                    Line(format!("pub const {styled_name}: u8 = {i};"))
4602                }
4603                (type_::Uint16(()), value::Uint16(i)) => {
4604                    Line(format!("pub const {styled_name}: u16 = {i};"))
4605                }
4606                (type_::Uint32(()), value::Uint32(i)) => {
4607                    Line(format!("pub const {styled_name}: u32 = {i};"))
4608                }
4609                (type_::Uint64(()), value::Uint64(i)) => {
4610                    Line(format!("pub const {styled_name}: u64 = {i};"))
4611                }
4612
4613                (type_::Float32(()), value::Float32(f)) => {
4614                    let literal = match f.classify() {
4615                        std::num::FpCategory::Nan => "f32::NAN".into(),
4616                        std::num::FpCategory::Infinite => {
4617                            if f.is_sign_positive() {
4618                                "f32::INFINITY".into()
4619                            } else {
4620                                "f32::NEG_INFINITY".into()
4621                            }
4622                        }
4623                        _ => format!("{f:e}"),
4624                    };
4625                    Line(format!("pub const {styled_name}: f32 = {literal};"))
4626                }
4627
4628                (type_::Float64(()), value::Float64(f)) => {
4629                    let literal = match f.classify() {
4630                        std::num::FpCategory::Nan => "f64::NAN".into(),
4631                        std::num::FpCategory::Infinite => {
4632                            if f.is_sign_positive() {
4633                                "f64::INFINITY".into()
4634                            } else {
4635                                "f64::NEG_INFINITY".into()
4636                            }
4637                        }
4638                        _ => format!("{f:e}"),
4639                    };
4640                    Line(format!("pub const {styled_name}: f64 = {literal};"))
4641                }
4642
4643                (type_::Enum(e), value::Enum(v)) => {
4644                    if let Some(node) = ctx.node_map.get(&e.get_type_id()) {
4645                        match node.which()? {
4646                            node::Enum(e) => {
4647                                let enumerants = e.get_enumerants()?;
4648                                if let Some(enumerant) = enumerants.try_get(u32::from(v)) {
4649                                    let variant =
4650                                        capitalize_first_letter(get_enumerant_name(enumerant)?);
4651                                    let type_string = typ.type_string(ctx, Leaf::Owned)?;
4652                                    Line(format!(
4653                                        "pub const {}: {} = {}::{};",
4654                                        styled_name, type_string, type_string, variant
4655                                    ))
4656                                } else {
4657                                    return Err(Error::failed(format!(
4658                                        "enumerant out of range: {v}"
4659                                    )));
4660                                }
4661                            }
4662                            _ => {
4663                                return Err(Error::failed(format!(
4664                                    "bad enum type ID: {}",
4665                                    e.get_type_id()
4666                                )));
4667                            }
4668                        }
4669                    } else {
4670                        return Err(Error::failed(format!(
4671                            "bad enum type ID: {}",
4672                            e.get_type_id()
4673                        )));
4674                    }
4675                }
4676
4677                (type_::Text(()), value::Text(t)) => Line(format!(
4678                    "pub const {styled_name}: &str = {:?};",
4679                    t?.to_str()?
4680                )),
4681                (type_::Data(()), value::Data(d)) => {
4682                    Line(format!("pub const {styled_name}: &[u8] = &{:?};", d?))
4683                }
4684
4685                (type_::List(_), value::List(v)) => {
4686                    generate_pointer_constant(ctx, &styled_name, typ, v)?
4687                }
4688                (type_::Struct(_), value::Struct(v)) => {
4689                    generate_pointer_constant(ctx, &styled_name, typ, v)?
4690                }
4691
4692                (type_::Interface(_t), value::Interface(())) => {
4693                    return Err(Error::unimplemented("interface constants".to_string()));
4694                }
4695                (type_::AnyPointer(_), value::AnyPointer(_pr)) => {
4696                    return Err(Error::unimplemented("anypointer constants".to_string()));
4697                }
4698
4699                _ => {
4700                    return Err(Error::failed("type does not match value".to_string()));
4701                }
4702            };
4703
4704            output.push(formatted_text);
4705        }
4706
4707        node::Annotation(annotation_reader) => {
4708            let is_generic = node_reader.get_is_generic();
4709            let params = node_reader.parameters_texts(ctx);
4710            let last_name = ctx.get_last_name(node_id)?;
4711            let mut interior = vec![];
4712            interior.push(Line(format!("pub const ID: u64 = 0x{node_id:x};")));
4713
4714            let ty = annotation_reader.get_type()?;
4715            if !is_generic {
4716                interior.push(Line(fmt!(ctx,
4717                    "pub fn get_type() -> {capnp}::introspect::Type {{ <{} as {capnp}::introspect::Introspect>::introspect() }}", ty.type_string(ctx, Leaf::Owned)?)));
4718            } else {
4719                interior.push(Line(fmt!(ctx,"pub fn get_type<{0}>() -> {capnp}::introspect::Type {1} {{ <{2} as {capnp}::introspect::Introspect>::introspect() }}", params.params, params.where_clause, ty.type_string(ctx, Leaf::Owned)?)));
4720            }
4721            output.push(Branch(vec![
4722                Line(format!("pub mod {last_name} {{")),
4723                indent(interior),
4724                Line("}".into()),
4725            ]));
4726        }
4727    }
4728
4729    Ok(Branch(output))
4730}
4731
4732// TODO: make indent take Into<FormattedText>, impl Into<FormattedText> for vec (branch)