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// We need this to work around the fact that Rust does not allow typedefs
2048// with unused type parameters.
2049fn get_ty_params_of_brand(
2050    ctx: &GeneratorContext,
2051    brand: schema_capnp::brand::Reader,
2052) -> ::capnp::Result<String> {
2053    let mut acc = HashSet::new();
2054    get_ty_params_of_brand_helper(ctx, &mut acc, brand)?;
2055    let mut result = String::new();
2056    for (scope_id, parameter_index) in acc.into_iter() {
2057        let node = ctx.node_map[&scope_id];
2058        let p = node.get_parameters()?.get(u32::from(parameter_index));
2059        result.push_str(p.get_name()?.to_str()?);
2060        result.push(',');
2061    }
2062
2063    Ok(result)
2064}
2065
2066fn get_ty_params_of_type_helper(
2067    ctx: &GeneratorContext,
2068    accumulator: &mut HashSet<(u64, u16)>,
2069    typ: schema_capnp::type_::Reader,
2070) -> ::capnp::Result<()> {
2071    use capnp::schema_capnp::type_;
2072    match typ.which()? {
2073        type_::Void(())
2074        | type_::Bool(())
2075        | type_::Int8(())
2076        | type_::Int16(())
2077        | type_::Int32(())
2078        | type_::Int64(())
2079        | type_::Uint8(())
2080        | type_::Uint16(())
2081        | type_::Uint32(())
2082        | type_::Uint64(())
2083        | type_::Float32(())
2084        | type_::Float64(())
2085        | type_::Text(_)
2086        | type_::Data(_) => {}
2087        type_::AnyPointer(p) => {
2088            match p.which()? {
2089                type_::any_pointer::Unconstrained(_) => (),
2090                type_::any_pointer::Parameter(p) => {
2091                    accumulator.insert((p.get_scope_id(), p.get_parameter_index()));
2092                }
2093                type_::any_pointer::ImplicitMethodParameter(_) => {
2094                    // XXX
2095                }
2096            }
2097        }
2098        type_::List(list) => {
2099            get_ty_params_of_type_helper(ctx, accumulator, list.get_element_type()?)?
2100        }
2101        type_::Enum(e) => {
2102            get_ty_params_of_brand_helper(ctx, accumulator, e.get_brand()?)?;
2103        }
2104        type_::Struct(s) => {
2105            get_ty_params_of_brand_helper(ctx, accumulator, s.get_brand()?)?;
2106        }
2107        type_::Interface(interf) => {
2108            get_ty_params_of_brand_helper(ctx, accumulator, interf.get_brand()?)?;
2109        }
2110    }
2111    Ok(())
2112}
2113
2114fn get_ty_params_of_brand_helper(
2115    ctx: &GeneratorContext,
2116    accumulator: &mut HashSet<(u64, u16)>,
2117    brand: schema_capnp::brand::Reader,
2118) -> ::capnp::Result<()> {
2119    for scope in brand.get_scopes()? {
2120        let scope_id = scope.get_scope_id();
2121        match scope.which()? {
2122            schema_capnp::brand::scope::Bind(bind) => {
2123                for binding in bind? {
2124                    match binding.which()? {
2125                        schema_capnp::brand::binding::Unbound(()) => {}
2126                        schema_capnp::brand::binding::Type(t) => {
2127                            get_ty_params_of_type_helper(ctx, accumulator, t?)?
2128                        }
2129                    }
2130                }
2131            }
2132            schema_capnp::brand::scope::Inherit(()) => {
2133                let parameters = ctx.node_map[&scope_id].get_parameters()?;
2134                for idx in 0..parameters.len() {
2135                    accumulator.insert((scope_id, idx as u16));
2136                }
2137            }
2138        }
2139    }
2140    Ok(())
2141}
2142
2143// A `type` newtype declaration (`type Vec3 = group { ... }`) is represented as a `type` node
2144// whose target is a struct pointing at a template node the newtype owns (its `scope_id` is the
2145// `type` node). We give such a newtype a semantic trait -- `vec3::Reader` -- so every use site
2146// shares one name. Returns the template node's id when `alias_id` is an inline group/union
2147// newtype, or a `type` aliasing one (`type Bar = Foo`), which shares Foo's template.
2148fn newtype_template_id(
2149    ctx: &GeneratorContext,
2150    alias_id: u64,
2151) -> ::capnp::Result<Option<u64>> {
2152    use capnp::schema_capnp::*;
2153    let Some(alias) = ctx.node_map.get(&alias_id) else {
2154        return Ok(None);
2155    };
2156    let node::Type(Ok(t)) = alias.which()? else {
2157        return Ok(None);
2158    };
2159    let type_::Struct(st) = t.which()? else {
2160        return Ok(None); // scalar newtype -- rendered via its underlying type
2161    };
2162    let template_id = st.get_type_id();
2163    let Some(template) = ctx.node_map.get(&template_id) else {
2164        return Ok(None);
2165    };
2166    if template.get_scope_id() != alias_id {
2167        // Not this node's own template. Either it aliases another inline newtype (recorded as
2168        // Type.typeId), whose template it shares, or it names a pre-existing struct.
2169        let aliased = t.get_type_id();
2170        if aliased != 0 {
2171            return newtype_template_id(ctx, aliased);
2172        }
2173        return Ok(None);
2174    }
2175    Ok(Some(template_id))
2176}
2177
2178// The inline newtype owning `alias_id`'s template: `alias_id` itself, or for `type Bar = Foo`
2179// (Foo an inline group/union newtype), Foo. None if `alias_id` isn't an inline newtype.
2180fn inline_newtype_owner(ctx: &GeneratorContext, alias_id: u64) -> ::capnp::Result<Option<u64>> {
2181    Ok(newtype_template_id(ctx, alias_id)?.map(|t| ctx.node_map[&t].get_scope_id()))
2182}
2183
2184// A non-union group newtype. Its fields are slots and/or nested-newtype members (group fields
2185// carrying a `Field.typeId`, surfaced as associated types). A plain anonymous group field is not
2186// yet supported.
2187fn group_newtype_template_id(
2188    ctx: &GeneratorContext,
2189    alias_id: u64,
2190) -> ::capnp::Result<Option<u64>> {
2191    use capnp::schema_capnp::*;
2192    let Some(template_id) = newtype_template_id(ctx, alias_id)? else {
2193        return Ok(None);
2194    };
2195    let node::Struct(tmpl) = ctx.node_map[&template_id].which()? else {
2196        return Ok(None);
2197    };
2198    if tmpl.get_discriminant_count() > 0 {
2199        return Ok(None); // union newtype -- handled separately
2200    }
2201    for field in tmpl.get_fields()? {
2202        if let field::Group(_) = field.which()? {
2203            if field.get_type_id() == 0 {
2204                return Ok(None); // anonymous group member -- not a newtype, deferred
2205            }
2206        }
2207    }
2208    Ok(Some(template_id))
2209}
2210
2211// A union newtype. Slot arms have use-site-independent `Which` payloads; a group arm is a nested
2212// newtype, surfaced through an associated type. An anonymous group arm is not yet supported.
2213fn union_newtype_template_id(
2214    ctx: &GeneratorContext,
2215    alias_id: u64,
2216) -> ::capnp::Result<Option<u64>> {
2217    use capnp::schema_capnp::*;
2218    let Some(template_id) = newtype_template_id(ctx, alias_id)? else {
2219        return Ok(None);
2220    };
2221    let node::Struct(tmpl) = ctx.node_map[&template_id].which()? else {
2222        return Ok(None);
2223    };
2224    if tmpl.get_discriminant_count() == 0 {
2225        return Ok(None); // not a union
2226    }
2227    for field in tmpl.get_fields()? {
2228        if let field::Group(_) = field.which()? {
2229            if field.get_type_id() == 0 {
2230                return Ok(None); // anonymous group arm -- not a newtype, deferred
2231            }
2232        }
2233    }
2234    Ok(Some(template_id))
2235}
2236
2237// One method of a newtype's Reader/Builder trait: its signature (the text after `fn`) plus how a
2238// delegating impl forwards to the inherent accessor. Reader getters take `&self`, but the inherent
2239// reader accessor consumes `self` (readers are `Copy`), so they forward through `(*self)`.
2240struct TraitMethod {
2241    signature: String,
2242    call_name: String,
2243    call_args: String,
2244    deref_self: bool,
2245}
2246
2247// The Reader- or Builder-side trait methods mirroring one template field's inherent accessors:
2248// a getter (both sides), the mutators (Builder side), and `has_` for a pointer field (both sides).
2249fn newtype_trait_methods(
2250    ctx: &GeneratorContext,
2251    field: &schema_capnp::field::Reader,
2252    is_reader: bool,
2253    include_getter: bool,
2254) -> ::capnp::Result<Vec<TraitMethod>> {
2255    use capnp::schema_capnp::*;
2256    let name = camel_to_snake_case(field.get_name()?.to_str()?);
2257    let is_pointer = match field.which()? {
2258        field::Slot(s) => s.get_type()?.is_pointer()?,
2259        field::Group(_) => false,
2260    };
2261    let mut methods = Vec::new();
2262
2263    // Union arms have no inherent getter -- they are read through `which()` -- so unions pass
2264    // include_getter = false; a delegating getter would just recurse.
2265    if is_reader {
2266        if include_getter {
2267            let (ret, _, _) = getter_text(ctx, field, true, true)?;
2268            methods.push(TraitMethod {
2269                signature: format!("get_{name}(&self) {ret}"),
2270                call_name: format!("get_{name}"),
2271                call_args: String::new(),
2272                deref_self: true,
2273            });
2274        }
2275    } else {
2276        if include_getter {
2277            // Builder getters consume `self` (they hand out the child builder), matching the
2278            // inherent accessor, so no deref.
2279            let (ret, _, _) = getter_text(ctx, field, false, true)?;
2280            methods.push(TraitMethod {
2281                signature: format!("get_{name}(self) {ret}"),
2282                call_name: format!("get_{name}"),
2283                call_args: String::new(),
2284                deref_self: false,
2285            });
2286        }
2287        for m in setter_methods(ctx, 0, &name, field)? {
2288            methods.push(TraitMethod {
2289                signature: m.signature(false),
2290                call_name: m.name,
2291                call_args: m.delegate_args,
2292                deref_self: false,
2293            });
2294        }
2295    }
2296    if is_pointer {
2297        methods.push(TraitMethod {
2298            signature: format!("has_{name}(&self) -> bool"),
2299            call_name: format!("has_{name}"),
2300            call_args: String::new(),
2301            deref_self: false,
2302        });
2303    }
2304    Ok(methods)
2305}
2306
2307// Emits the trait module for a newtype: a flat group (`generate_flat_newtype_trait`) or a union
2308// (`generate_union_newtype_trait`). Exactly one applies (or neither); each no-ops otherwise.
2309fn generate_newtype_trait(
2310    ctx: &GeneratorContext,
2311    node_id: u64,
2312    node_name: &str,
2313) -> ::capnp::Result<FormattedText> {
2314    if let Some(owner) = inline_newtype_owner(ctx, node_id)? {
2315        if owner != node_id {
2316            // `type Bar = Foo` with Foo an inline newtype: Bar's module re-exports Foo's, so
2317            // `bar::Reader` *is* `foo::Reader` and Bar's use sites implement Foo's traits.
2318            return Ok(Branch(vec![
2319                BlankLine,
2320                Line(format!("pub mod {} {{", module_name(node_name))),
2321                indent(Line(format!("pub use {}::*;", ctx.get_qualified_module(owner)))),
2322                line("}"),
2323            ]));
2324        }
2325    }
2326    Ok(Branch(vec![
2327        generate_group_newtype_trait(ctx, node_id, node_name)?,
2328        generate_union_newtype_trait(ctx, node_id, node_name)?,
2329    ]))
2330}
2331
2332// Recursive leaf count of a newtype template: a slot is one leaf; a nested-newtype member expands
2333// to its own leaves. The erased carrier uses this to slice its offset table.
2334fn count_newtype_leaves(ctx: &GeneratorContext, template_id: u64) -> ::capnp::Result<usize> {
2335    use capnp::schema_capnp::*;
2336    let node::Struct(tmpl) = ctx.node_map[&template_id].which()? else {
2337        return Ok(0);
2338    };
2339    let mut n = 0;
2340    for field in tmpl.get_fields()? {
2341        match field.which()? {
2342            field::Slot(_) => n += 1,
2343            field::Group(_) => {
2344                if let Some(nested) = newtype_template_id(ctx, field.get_type_id())? {
2345                    n += count_newtype_leaves(ctx, nested)?;
2346                }
2347            }
2348        }
2349    }
2350    Ok(n)
2351}
2352
2353// Collects a use site's leaf offsets in template (tree) order, recursing through nested-newtype
2354// members, matching the instance's fields to the template by name. A leaf absent from the instance
2355// (an incomplete `@[...]`) gets the sentinel `0xffff_ffff`.
2356fn collect_leaf_offsets(
2357    ctx: &GeneratorContext,
2358    template_id: u64,
2359    instance_id: u64,
2360    out: &mut Vec<String>,
2361) -> ::capnp::Result<()> {
2362    use capnp::schema_capnp::*;
2363    let node::Struct(tmpl) = ctx.node_map[&template_id].which()? else {
2364        return Ok(());
2365    };
2366    let node::Struct(instance) = ctx.node_map[&instance_id].which()? else {
2367        return Ok(());
2368    };
2369    let mut inst_fields = ::std::collections::HashMap::new();
2370    for f in instance.get_fields()? {
2371        inst_fields.insert(f.get_name()?.to_str()?.to_string(), f);
2372    }
2373    for field in tmpl.get_fields()? {
2374        let raw = field.get_name()?.to_str()?;
2375        match field.which()? {
2376            field::Slot(_) => match inst_fields.get(raw).map(|f| f.which()) {
2377                Some(Ok(field::Slot(s))) => out.push(s.get_offset().to_string()),
2378                _ => out.push("0xffff_ffff".to_string()),
2379            },
2380            field::Group(_) => {
2381                let Some(nested_tmpl) = newtype_template_id(ctx, field.get_type_id())? else {
2382                    continue;
2383                };
2384                match inst_fields.get(raw).map(|f| f.which()) {
2385                    Some(Ok(field::Group(ig))) => {
2386                        collect_leaf_offsets(ctx, nested_tmpl, ig.get_type_id(), out)?
2387                    }
2388                    _ => {
2389                        for _ in 0..count_newtype_leaves(ctx, nested_tmpl)? {
2390                            out.push("0xffff_ffff".to_string());
2391                        }
2392                    }
2393                }
2394            }
2395        }
2396    }
2397    Ok(())
2398}
2399
2400// The erased carrier for a (non-union) group newtype: a concrete `AnyReader<'a>` holding a
2401// `StructReader` plus a runtime offset table, so one type serves every use site (unlike `&dyn`).
2402// A slot accessor indexes `self.offsets[i]` via `getter_text_at`; a nested-newtype member returns
2403// the member's own `AnyReader` over a sub-slice of the table. It impls the newtype's `Reader`
2404// trait, so `impl Reader` bounds accept both the concrete per-site type and the erased form.
2405fn any_reader_items(
2406    ctx: &GeneratorContext,
2407    template_id: u64,
2408) -> ::capnp::Result<Vec<FormattedText>> {
2409    use capnp::schema_capnp::*;
2410    let node::Struct(tmpl) = ctx.node_map[&template_id].which()? else {
2411        return Ok(vec![]);
2412    };
2413    if tmpl.get_discriminant_count() > 0 {
2414        return Ok(vec![]); // union newtype -- separate carrier
2415    }
2416
2417    let mut inherent = Vec::new();
2418    let mut trait_methods = Vec::new();
2419    let mut defaults = Vec::new();
2420    let mut cursor = 0usize;
2421    for field in tmpl.get_fields()? {
2422        let name = camel_to_snake_case(field.get_name()?.to_str()?);
2423        if let field::Group(_) = field.which()? {
2424            let assoc = capitalize_first_letter(field.get_name()?.to_str()?);
2425            let nested_mod = ctx.get_qualified_module(field.get_type_id());
2426            let Some(nested_tmpl) = newtype_template_id(ctx, field.get_type_id())? else {
2427                return Ok(vec![]);
2428            };
2429            let count = count_newtype_leaves(ctx, nested_tmpl)?;
2430            let (start, end) = (cursor, cursor + count);
2431            inherent.push(Line(format!(
2432                "pub fn get_{name}(&self) -> {nested_mod}::AnyReader<'a> {{ {nested_mod}::AnyReader::new(self.reader, &self.offsets[{start}..{end}]) }}"
2433            )));
2434            trait_methods.push(Line(format!("type {assoc} = {nested_mod}::AnyReader<'a>;")));
2435            trait_methods.push(Line(format!(
2436                "fn get_{name}(&self) -> Self::{assoc} {{ self.get_{name}() }}"
2437            )));
2438            cursor += count;
2439        } else {
2440            let off = format!("self.offsets[{cursor}] as usize");
2441            let (ret, mut body, def) = getter_text_at(ctx, &field, true, true, Some(&off))?;
2442            if let Some(d) = def {
2443                defaults.push(d);
2444            }
2445            // An unmapped leaf (sentinel offset) reads its default rather than dereferencing the
2446            // sentinel -- matching C++, and 32-bit-safe (the out-of-range read would overflow).
2447            if is_scalar_data_field(&field)? {
2448                if let (field::Slot(s), FormattedText::Line(expr)) = (field.which()?, &body) {
2449                    let expr = expr.clone();
2450                    let dflt = prim_default_value(&s.get_default_value()?)?;
2451                    body = Line(format!(
2452                        "if self.offsets[{cursor}] == 0xffff_ffffu32 {{ {dflt} }} else {{ {expr} }}"
2453                    ));
2454                }
2455            }
2456            inherent.push(Line(format!("pub fn get_{name}(&self) {ret} {{")));
2457            inherent.push(indent(body));
2458            inherent.push(line("}"));
2459            trait_methods.push(Line(format!("fn get_{name}(&self) {ret} {{ self.get_{name}() }}")));
2460            if let field::Slot(s) = field.which()? {
2461                if s.get_type()?.is_pointer()? {
2462                    inherent.push(Line(format!(
2463                        "pub fn has_{name}(&self) -> bool {{ !self.reader.get_pointer_field(self.offsets[{cursor}] as usize).is_null() }}"
2464                    )));
2465                    trait_methods.push(Line(format!(
2466                        "fn has_{name}(&self) -> bool {{ self.has_{name}() }}"
2467                    )));
2468                }
2469            }
2470            cursor += 1;
2471        }
2472    }
2473
2474    let sr = fmt!(ctx, "{capnp}::private::layout::StructReader<'a>");
2475    let mut items = vec![
2476        BlankLine,
2477        line("#[derive(Clone, Copy)]"),
2478        Line(format!(
2479            "pub struct AnyReader<'a> {{ reader: {sr}, offsets: &'a [u32] }}"
2480        )),
2481        line("impl<'a> AnyReader<'a> {"),
2482        indent(vec![
2483            Line(format!(
2484                "pub fn new(reader: {sr}, offsets: &'a [u32]) -> Self {{ AnyReader {{ reader, offsets }} }}"
2485            )),
2486            Branch(inherent),
2487        ]),
2488        line("}"),
2489        line("impl<'a> Reader<'a> for AnyReader<'a> {"),
2490        indent(trait_methods),
2491        line("}"),
2492    ];
2493    if !defaults.is_empty() {
2494        items.push(line("mod _private {"));
2495        items.push(indent(defaults));
2496        items.push(line("}"));
2497    }
2498    Ok(items)
2499}
2500
2501// The erased carrier for a union newtype: like `AnyReader` but with a runtime discriminant offset.
2502// `which()` reads the discriminant and builds the shared `Which`, with a group arm yielding the
2503// member's own `AnyReader` over an offset sub-slice -- so the erased union unifies across use sites
2504// (which `&dyn` cannot, given the arm associated types).
2505fn union_any_reader_items(
2506    ctx: &GeneratorContext,
2507    template_id: u64,
2508) -> ::capnp::Result<Vec<FormattedText>> {
2509    use capnp::schema_capnp::*;
2510    let node::Struct(tmpl) = ctx.node_map[&template_id].which()? else {
2511        return Ok(vec![]);
2512    };
2513    if tmpl.get_discriminant_count() == 0 {
2514        return Ok(vec![]);
2515    }
2516
2517    let mut arms = Vec::new();
2518    let mut assoc = Vec::new();
2519    let mut group_variants = Vec::new();
2520    let mut which_type_params = Vec::new();
2521    let mut has_inherent = Vec::new();
2522    let mut has_trait = Vec::new();
2523    let mut needs_lifetime = false;
2524    let mut cursor = 0usize;
2525    for field in tmpl.get_fields()? {
2526        let variant = capitalize_first_letter(field.get_name()?.to_str()?);
2527        let name = camel_to_snake_case(field.get_name()?.to_str()?);
2528        let dvalue = field.get_discriminant_value();
2529        if let field::Group(_) = field.which()? {
2530            let nested_mod = ctx.get_qualified_module(field.get_type_id());
2531            let Some(nested_tmpl) = newtype_template_id(ctx, field.get_type_id())? else {
2532                return Ok(vec![]);
2533            };
2534            let count = count_newtype_leaves(ctx, nested_tmpl)?;
2535            let (start, end) = (cursor, cursor + count);
2536            arms.push(Line(format!(
2537                "{dvalue} => ::core::result::Result::Ok(Which::{variant}({nested_mod}::AnyReader::new(self.reader, &self.offsets[{start}..{end}]))),"
2538            )));
2539            assoc.push(Line(format!("type {variant} = {nested_mod}::AnyReader<'a>;")));
2540            group_variants.push(variant.clone());
2541            which_type_params.push(format!("{nested_mod}::AnyReader<'a>"));
2542            cursor += count;
2543        } else {
2544            let off = format!("self.offsets[{cursor}] as usize");
2545            let (_, getter_code, _) = getter_text_at(ctx, &field, true, false, Some(&off))?;
2546            let FormattedText::Line(payload) = getter_code else {
2547                return Ok(vec![]); // option-wrapped arm -- unsupported in the erased union for now
2548            };
2549            arms.push(Line(format!(
2550                "{dvalue} => ::core::result::Result::Ok(Which::{variant}({payload})),"
2551            )));
2552            if let field::Slot(s) = field.which()? {
2553                if s.get_type()?.is_pointer()? {
2554                    needs_lifetime = true;
2555                    has_inherent.push(Line(format!(
2556                        "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() }}"
2557                    )));
2558                    has_trait.push(Line(format!(
2559                        "fn has_{name}(&self) -> bool {{ self.has_{name}() }}"
2560                    )));
2561                }
2562            }
2563            cursor += 1;
2564        }
2565    }
2566    arms.push(line(
2567        "x => ::core::result::Result::Err(::capnp::NotInSchema(x)),",
2568    ));
2569
2570    let inherent_ret = union_which_generics(needs_lifetime, &which_type_params);
2571    let self_ret = union_which_generics(
2572        needs_lifetime,
2573        &group_variants
2574            .iter()
2575            .map(|v| format!("Self::{v}"))
2576            .collect::<Vec<_>>(),
2577    );
2578
2579    let sr = fmt!(ctx, "{capnp}::private::layout::StructReader<'a>");
2580    let which_method = Branch(vec![
2581        Line(format!(
2582            "pub fn which(&self) -> ::core::result::Result<Which{inherent_ret}, ::capnp::NotInSchema> {{"
2583        )),
2584        indent(vec![
2585            line("match self.reader.get_data_field::<u16>(self.disc_offset as usize) {"),
2586            indent(arms),
2587            line("}"),
2588        ]),
2589        line("}"),
2590    ]);
2591
2592    let mut trait_items = assoc;
2593    trait_items.push(Line(format!(
2594        "fn which(&self) -> ::core::result::Result<Which{self_ret}, ::capnp::NotInSchema> {{ self.which() }}"
2595    )));
2596    trait_items.extend(has_trait);
2597
2598    Ok(vec![
2599        BlankLine,
2600        line("#[derive(Clone, Copy)]"),
2601        Line(format!(
2602            "pub struct AnyReader<'a> {{ reader: {sr}, disc_offset: u32, offsets: &'a [u32] }}"
2603        )),
2604        line("impl<'a> AnyReader<'a> {"),
2605        indent(vec![
2606            Line(format!(
2607                "pub fn new(reader: {sr}, disc_offset: u32, offsets: &'a [u32]) -> Self {{ AnyReader {{ reader, disc_offset, offsets }} }}"
2608            )),
2609            which_method,
2610            Branch(has_inherent),
2611        ]),
2612        line("}"),
2613        line("impl<'a> Reader<'a> for AnyReader<'a> {"),
2614        indent(trait_items),
2615        line("}"),
2616    ])
2617}
2618
2619// The erased mutable carrier for a (non-union) group newtype: `AnyBuilder<'a>` over a
2620// `StructBuilder` and a runtime offset table. Setters/init write at `self.offsets[i]`; a
2621// nested-newtype member yields the member's own `AnyBuilder` over a sub-slice.
2622fn any_builder_items(
2623    ctx: &GeneratorContext,
2624    template_id: u64,
2625) -> ::capnp::Result<Vec<FormattedText>> {
2626    use capnp::schema_capnp::*;
2627    let node::Struct(tmpl) = ctx.node_map[&template_id].which()? else {
2628        return Ok(vec![]);
2629    };
2630    if tmpl.get_discriminant_count() > 0 {
2631        return Ok(vec![]); // union AnyBuilder -- deferred
2632    }
2633
2634    let mut inherent = Vec::new();
2635    let mut trait_methods = Vec::new();
2636    let mut cursor = 0usize;
2637    for field in tmpl.get_fields()? {
2638        let name = camel_to_snake_case(field.get_name()?.to_str()?);
2639        if let field::Group(_) = field.which()? {
2640            let assoc = capitalize_first_letter(field.get_name()?.to_str()?);
2641            let nested_mod = ctx.get_qualified_module(field.get_type_id());
2642            let Some(nested_tmpl) = newtype_template_id(ctx, field.get_type_id())? else {
2643                return Ok(vec![]);
2644            };
2645            let count = count_newtype_leaves(ctx, nested_tmpl)?;
2646            let (start, end) = (cursor, cursor + count);
2647            for verb in ["get", "init"] {
2648                inherent.push(Line(format!(
2649                    "pub fn {verb}_{name}(self) -> {nested_mod}::AnyBuilder<'a> {{ {nested_mod}::AnyBuilder::new(self.builder, &self.offsets[{start}..{end}]) }}"
2650                )));
2651            }
2652            trait_methods.push(Line(format!("type {assoc} = {nested_mod}::AnyBuilder<'a>;")));
2653            trait_methods.push(Line(format!(
2654                "fn get_{name}(self) -> Self::{assoc} {{ self.get_{name}() }}"
2655            )));
2656            trait_methods.push(Line(format!(
2657                "fn init_{name}(self) -> Self::{assoc} {{ self.init_{name}() }}"
2658            )));
2659            cursor += count;
2660        } else {
2661            let off = format!("self.offsets[{cursor}] as usize");
2662            let guard = unmapped_panic(cursor, &name);
2663            // Builder getter: a scalar reads its default at the sentinel; a pointer getter panics
2664            // (the builder read is unchecked and there's no empty builder to return).
2665            let (ret, body, _) = getter_text_at(ctx, &field, false, true, Some(&off))?;
2666            let get_body = if is_scalar_data_field(&field)? {
2667                if let field::Slot(s) = field.which()? {
2668                    if let FormattedText::Line(expr) = &body {
2669                        let expr = expr.clone();
2670                        let dflt = prim_default_value(&s.get_default_value()?)?;
2671                        Line(format!(
2672                            "if self.offsets[{cursor}] == 0xffff_ffffu32 {{ {dflt} }} else {{ {expr} }}"
2673                        ))
2674                    } else {
2675                        body
2676                    }
2677                } else {
2678                    body
2679                }
2680            } else {
2681                Branch(vec![Line(guard.clone()), body])
2682            };
2683            inherent.push(Line(format!("pub fn get_{name}(self) {ret} {{")));
2684            inherent.push(indent(get_body));
2685            inherent.push(line("}"));
2686            trait_methods.push(Line(format!("fn get_{name}(self) {ret} {{ self.get_{name}() }}")));
2687            for m in setter_methods_at(ctx, "0", &name, &field, Some(&off))? {
2688                let sig_inherent = m.signature(true);
2689                let sig_trait = m.signature(false);
2690                let call = format!("self.{}({})", m.name, m.delegate_args);
2691                inherent.push(Line(format!("pub fn {sig_inherent} {{")));
2692                inherent.push(indent(Branch(vec![Line(guard.clone()), m.body])));
2693                inherent.push(line("}"));
2694                trait_methods.push(Line(format!("fn {sig_trait} {{ {call} }}")));
2695            }
2696            if let field::Slot(s) = field.which()? {
2697                if s.get_type()?.is_pointer()? {
2698                    inherent.push(Line(format!(
2699                        "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) }}"
2700                    )));
2701                    trait_methods.push(Line(format!(
2702                        "fn has_{name}(&self) -> bool {{ self.has_{name}() }}"
2703                    )));
2704                }
2705            }
2706            cursor += 1;
2707        }
2708    }
2709
2710    let sb = fmt!(ctx, "{capnp}::private::layout::StructBuilder<'a>");
2711    Ok(vec![
2712        BlankLine,
2713        Line(format!(
2714            "pub struct AnyBuilder<'a> {{ builder: {sb}, offsets: &'a [u32] }}"
2715        )),
2716        line("impl<'a> AnyBuilder<'a> {"),
2717        indent(vec![
2718            Line(format!(
2719                "pub fn new(builder: {sb}, offsets: &'a [u32]) -> Self {{ AnyBuilder {{ builder, offsets }} }}"
2720            )),
2721            Branch(inherent),
2722        ]),
2723        line("}"),
2724        line("impl<'a> Builder<'a> for AnyBuilder<'a> {"),
2725        indent(trait_methods),
2726        line("}"),
2727    ])
2728}
2729
2730// The erased mutable carrier for a union newtype: `AnyBuilder` with a runtime discriminant offset.
2731// Each arm setter writes the discriminant at `self.disc_offset` (via `setter_methods_at`); a group
2732// arm's `init_` writes it then hands out the member's own `AnyBuilder` over an offset sub-slice.
2733fn union_any_builder_items(
2734    ctx: &GeneratorContext,
2735    template_id: u64,
2736) -> ::capnp::Result<Vec<FormattedText>> {
2737    use capnp::schema_capnp::*;
2738    let node::Struct(tmpl) = ctx.node_map[&template_id].which()? else {
2739        return Ok(vec![]);
2740    };
2741    if tmpl.get_discriminant_count() == 0 {
2742        return Ok(vec![]);
2743    }
2744
2745    let mut inherent = Vec::new();
2746    let mut assoc = Vec::new();
2747    let mut trait_methods = Vec::new();
2748    let mut cursor = 0usize;
2749    for field in tmpl.get_fields()? {
2750        let variant = capitalize_first_letter(field.get_name()?.to_str()?);
2751        let name = camel_to_snake_case(field.get_name()?.to_str()?);
2752        let dvalue = field.get_discriminant_value();
2753        if let field::Group(_) = field.which()? {
2754            let nested_mod = ctx.get_qualified_module(field.get_type_id());
2755            let Some(nested_tmpl) = newtype_template_id(ctx, field.get_type_id())? else {
2756                return Ok(vec![]);
2757            };
2758            let count = count_newtype_leaves(ctx, nested_tmpl)?;
2759            let (start, end) = (cursor, cursor + count);
2760            inherent.push(Line(format!(
2761                "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}]) }}"
2762            )));
2763            assoc.push(Line(format!("type {variant} = {nested_mod}::AnyBuilder<'a>;")));
2764            trait_methods.push(Line(format!(
2765                "fn init_{name}(self) -> Self::{variant} {{ self.init_{name}() }}"
2766            )));
2767            cursor += count;
2768        } else {
2769            let off = format!("self.offsets[{cursor}] as usize");
2770            let guard = unmapped_panic(cursor, &name);
2771            for m in setter_methods_at(ctx, "self.disc_offset as usize", &name, &field, Some(&off))? {
2772                let sig_inherent = m.signature(true);
2773                let sig_trait = m.signature(false);
2774                let call = format!("self.{}({})", m.name, m.delegate_args);
2775                inherent.push(Line(format!("pub fn {sig_inherent} {{")));
2776                inherent.push(indent(Branch(vec![Line(guard.clone()), m.body])));
2777                inherent.push(line("}"));
2778                trait_methods.push(Line(format!("fn {sig_trait} {{ {call} }}")));
2779            }
2780            if let field::Slot(s) = field.which()? {
2781                if s.get_type()?.is_pointer()? {
2782                    inherent.push(Line(format!(
2783                        "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) }}"
2784                    )));
2785                    trait_methods.push(Line(format!(
2786                        "fn has_{name}(&self) -> bool {{ self.has_{name}() }}"
2787                    )));
2788                }
2789            }
2790            cursor += 1;
2791        }
2792    }
2793
2794    let sb = fmt!(ctx, "{capnp}::private::layout::StructBuilder<'a>");
2795    let mut trait_items = assoc;
2796    trait_items.extend(trait_methods);
2797    Ok(vec![
2798        BlankLine,
2799        Line(format!(
2800            "pub struct AnyBuilder<'a> {{ builder: {sb}, disc_offset: u32, offsets: &'a [u32] }}"
2801        )),
2802        line("impl<'a> AnyBuilder<'a> {"),
2803        indent(vec![
2804            Line(format!(
2805                "pub fn new(builder: {sb}, disc_offset: u32, offsets: &'a [u32]) -> Self {{ AnyBuilder {{ builder, disc_offset, offsets }} }}"
2806            )),
2807            Branch(inherent),
2808        ]),
2809        line("}"),
2810        line("impl<'a> Builder<'a> for AnyBuilder<'a> {"),
2811        indent(trait_items),
2812        line("}"),
2813    ])
2814}
2815
2816// Emits `pub mod <name> { pub trait Reader<'a> {...} pub trait Builder<'a> {...} }` for a non-union
2817// group newtype. Slot fields mirror the per-use-site accessors; a nested-newtype member (a group
2818// field) is surfaced as an associated type bounded by that newtype's trait, plus a getter.
2819fn generate_group_newtype_trait(
2820    ctx: &GeneratorContext,
2821    node_id: u64,
2822    node_name: &str,
2823) -> ::capnp::Result<FormattedText> {
2824    use capnp::schema_capnp::*;
2825    let Some(template_id) = group_newtype_template_id(ctx, node_id)? else {
2826        return Ok(Branch(vec![]));
2827    };
2828    let node::Struct(tmpl) = ctx.node_map[&template_id].which()? else {
2829        return Ok(Branch(vec![]));
2830    };
2831
2832    let mut reader_items = Vec::new();
2833    let mut builder_items = Vec::new();
2834    for field in tmpl.get_fields()? {
2835        if let field::Group(_) = field.which()? {
2836            // Nested newtype member: an associated type bounded by the member's trait, plus a
2837            // getter (and builder init) -- no whole-value setter, same as a struct field.
2838            let name = camel_to_snake_case(field.get_name()?.to_str()?);
2839            let assoc = capitalize_first_letter(field.get_name()?.to_str()?);
2840            let nested = ctx.get_qualified_module(field.get_type_id());
2841            reader_items.push(Line(format!("type {assoc}: {nested}::Reader<'a>;")));
2842            reader_items.push(Line(format!("fn get_{name}(&self) -> Self::{assoc};")));
2843            builder_items.push(Line(format!("type {assoc}: {nested}::Builder<'a>;")));
2844            builder_items.push(Line(format!("fn get_{name}(self) -> Self::{assoc};")));
2845            builder_items.push(Line(format!("fn init_{name}(self) -> Self::{assoc};")));
2846        } else {
2847            for m in newtype_trait_methods(ctx, &field, true, true)? {
2848                reader_items.push(Line(format!("fn {};", m.signature)));
2849            }
2850            for m in newtype_trait_methods(ctx, &field, false, true)? {
2851                builder_items.push(Line(format!("fn {};", m.signature)));
2852            }
2853        }
2854    }
2855
2856    let mut module_items = vec![
2857        line("pub trait Reader<'a> {"),
2858        indent(reader_items),
2859        line("}"),
2860        line("pub trait Builder<'a> {"),
2861        indent(builder_items),
2862        line("}"),
2863    ];
2864    module_items.extend(any_reader_items(ctx, template_id)?);
2865    module_items.extend(any_builder_items(ctx, template_id)?);
2866
2867    Ok(Branch(vec![
2868        BlankLine,
2869        Line(format!("pub mod {} {{", module_name(node_name))),
2870        indent(module_items),
2871        line("}"),
2872    ]))
2873}
2874
2875// Emits `impl<'a> <newtype>::Reader<'a> for <instance>::Reader<'a>` at a use site, giving the
2876// stamped group's per-instance module the newtype's identity. Each method delegates to the
2877// inherent accessor (inherent methods win over trait methods in method resolution, so this is
2878// not recursive), which keeps offsets/defaults/pointer handling in one place.
2879fn generate_newtype_impl(
2880    ctx: &GeneratorContext,
2881    alias_id: u64,
2882    instance_id: u64,
2883) -> ::capnp::Result<FormattedText> {
2884    Ok(Branch(vec![
2885        generate_group_newtype_impl(ctx, alias_id, instance_id)?,
2886        generate_union_newtype_impl(ctx, alias_id, instance_id)?,
2887    ]))
2888}
2889
2890fn generate_group_newtype_impl(
2891    ctx: &GeneratorContext,
2892    alias_id: u64,
2893    instance_id: u64,
2894) -> ::capnp::Result<FormattedText> {
2895    use capnp::schema_capnp::*;
2896    let Some(template_id) = group_newtype_template_id(ctx, alias_id)? else {
2897        return Ok(Branch(vec![]));
2898    };
2899    let node::Struct(tmpl) = ctx.node_map[&template_id].which()? else {
2900        return Ok(Branch(vec![]));
2901    };
2902    let node::Struct(instance) = ctx.node_map[&instance_id].which()? else {
2903        return Ok(Branch(vec![]));
2904    };
2905
2906    // An incomplete `@[...]` mapping omits trailing fields from the instance; those have no
2907    // inherent accessor to delegate to, so skip the trait for such a use site until the erased
2908    // carrier (which reads defaults) lands. Index the instance's fields by name so nested members
2909    // can find their per-instance group node.
2910    let mut instance_fields = ::std::collections::HashMap::new();
2911    for field in instance.get_fields()? {
2912        instance_fields.insert(field.get_name()?.to_str()?.to_string(), field);
2913    }
2914
2915    let mut trait_ok = true;
2916    let mut reader_items = Vec::new();
2917    let mut builder_items = Vec::new();
2918    for field in tmpl.get_fields()? {
2919        let raw_name = field.get_name()?.to_str()?;
2920        let Some(inst_field) = instance_fields.get(raw_name) else {
2921            // Unmapped leaf (incomplete `@[...]`): a scalar reads its default and its setter
2922            // panics -- matching C++'s default read + "not mapped" assert. Anything else can't be
2923            // synthesized in the concrete trait here, so skip just the trait for this use site --
2924            // the erased `AnyReader` (via the offset table's sentinels) still reads it.
2925            if is_scalar_data_field(&field)? {
2926                let field::Slot(s) = field.which()? else {
2927                    trait_ok = false;
2928                    continue;
2929                };
2930                let dflt = prim_default_value(&s.get_default_value()?)?;
2931                let not_mapped =
2932                    format!("panic!(\"field '{raw_name}' is not mapped at this use site\")");
2933                for m in newtype_trait_methods(ctx, &field, true, true)? {
2934                    reader_items.push(Line(format!("fn {} {{ {dflt} }}", m.signature)));
2935                }
2936                for m in newtype_trait_methods(ctx, &field, false, true)? {
2937                    let body = if m.call_name.starts_with("get_") {
2938                        dflt.clone()
2939                    } else if m.call_args.is_empty() {
2940                        not_mapped.clone()
2941                    } else if m.call_args.contains(',') {
2942                        format!("let _ = ({}); {not_mapped}", m.call_args)
2943                    } else {
2944                        // Consume the arg so the panic-only body doesn't warn as unused.
2945                        format!("let _ = {}; {not_mapped}", m.call_args)
2946                    };
2947                    builder_items.push(Line(format!("fn {} {{ {body} }}", m.signature)));
2948                }
2949            } else {
2950                trait_ok = false;
2951            }
2952            continue;
2953        };
2954        // Delegate to the inherent accessor: inherent methods win over trait methods in method
2955        // resolution, so this forwards rather than recurses, keeping offsets/defaults in one place.
2956        if let field::Group(_) = field.which()? {
2957            let field::Group(inst_group) = inst_field.which()? else {
2958                return Ok(Branch(vec![]));
2959            };
2960            let name = camel_to_snake_case(raw_name);
2961            let assoc = capitalize_first_letter(raw_name);
2962            let inst_mod = ctx.get_qualified_module(inst_group.get_type_id());
2963            reader_items.push(Line(format!("type {assoc} = {inst_mod}::Reader<'a>;")));
2964            reader_items.push(Line(format!(
2965                "fn get_{name}(&self) -> Self::{assoc} {{ (*self).get_{name}() }}"
2966            )));
2967            builder_items.push(Line(format!("type {assoc} = {inst_mod}::Builder<'a>;")));
2968            builder_items.push(Line(format!(
2969                "fn get_{name}(self) -> Self::{assoc} {{ self.get_{name}() }}"
2970            )));
2971            builder_items.push(Line(format!(
2972                "fn init_{name}(self) -> Self::{assoc} {{ self.init_{name}() }}"
2973            )));
2974        } else {
2975            for m in newtype_trait_methods(ctx, &field, true, true)? {
2976                let recv = if m.deref_self { "(*self)" } else { "self" };
2977                reader_items.push(Line(format!(
2978                    "fn {} {{ {recv}.{}({}) }}",
2979                    m.signature, m.call_name, m.call_args
2980                )));
2981            }
2982            for m in newtype_trait_methods(ctx, &field, false, true)? {
2983                let recv = if m.deref_self { "(*self)" } else { "self" };
2984                builder_items.push(Line(format!(
2985                    "fn {} {{ {recv}.{}({}) }}",
2986                    m.signature, m.call_name, m.call_args
2987                )));
2988            }
2989        }
2990    }
2991
2992    let reader_methods = reader_items;
2993    let builder_methods = builder_items;
2994    let trait_path = ctx.get_qualified_module(alias_id);
2995    let concrete = ctx.get_qualified_module(instance_id);
2996
2997    // Erased-carrier wiring: a per-use-site offset table (recursively over nested members) plus
2998    // `as_any()` on the concrete reader -- through the public `IntoInternalStructReader`, since the
2999    // inner field is private at this impl site.
3000    let mut any_items = Vec::new();
3001    {
3002        let mut offsets = Vec::new();
3003        collect_leaf_offsets(ctx, template_id, instance_id, &mut offsets)?;
3004        let n = offsets.len();
3005        let table = format!(
3006            "{}_OFFSETS",
3007            camel_to_snake_case(ctx.get_last_name(instance_id)?).to_ascii_uppercase()
3008        );
3009        any_items.push(BlankLine);
3010        any_items.push(Line(format!(
3011            "static {table}: [u32; {n}] = [{}];",
3012            offsets.join(", ")
3013        )));
3014        any_items.push(Line(format!("impl<'a> {concrete}::Reader<'a> {{")));
3015        any_items.push(indent(Line(fmt!(ctx,
3016            "pub fn as_any(self) -> {trait_path}::AnyReader<'a> {{ {trait_path}::AnyReader::new({capnp}::traits::IntoInternalStructReader::into_internal_struct_reader(self), &{table}) }}"
3017        ))));
3018        any_items.push(line("}"));
3019        any_items.push(Line(format!("impl<'a> {concrete}::Builder<'a> {{")));
3020        any_items.push(indent(Line(fmt!(ctx,
3021            "pub fn as_any(self) -> {trait_path}::AnyBuilder<'a> {{ {trait_path}::AnyBuilder::new({capnp}::traits::IntoInternalStructBuilder::into_internal_struct_builder(self), &{table}) }}"
3022        ))));
3023        any_items.push(line("}"));
3024    }
3025
3026    // The concrete trait impls require every leaf mappable (see `trait_ok`); the erased carrier
3027    // (`any_items`) is always emitted -- it reads unmapped leaves as defaults via the offset table.
3028    let mut out = Vec::new();
3029    if trait_ok {
3030        out.push(BlankLine);
3031        out.push(Line(format!(
3032            "impl<'a> {trait_path}::Reader<'a> for {concrete}::Reader<'a> {{"
3033        )));
3034        out.push(indent(reader_methods));
3035        out.push(line("}"));
3036        out.push(BlankLine);
3037        out.push(Line(format!(
3038            "impl<'a> {trait_path}::Builder<'a> for {concrete}::Builder<'a> {{"
3039        )));
3040        out.push(indent(builder_methods));
3041        out.push(line("}"));
3042    }
3043    out.push(Branch(any_items));
3044    Ok(Branch(out))
3045}
3046
3047// The `Which` enum variants for a union newtype and whether any arm payload borrows (so the enum
3048// needs a `<'a>`). Payload types come straight from the arm getter, so they match the per-instance
3049// `Which` the impl maps from.
3050// The `<...>` on a union newtype's `Which`: an optional `'a` (when a slot arm's payload borrows)
3051// followed by one type parameter per group arm (`params`).
3052fn union_which_generics(needs_lifetime: bool, params: &[String]) -> String {
3053    let mut all = Vec::new();
3054    if needs_lifetime {
3055        all.push("'a".to_string());
3056    }
3057    all.extend(params.iter().cloned());
3058    if all.is_empty() {
3059        String::new()
3060    } else {
3061        format!("<{}>", all.join(", "))
3062    }
3063}
3064
3065// Emits `pub mod <name> { pub enum Which<...> {...} pub trait Reader<'a> {...} pub trait Builder }`
3066// for a union newtype. A slot arm has a concrete `Which` payload; a group arm (a nested newtype) is
3067// a `Which` type parameter backed by an associated type bounded by the arm's trait.
3068fn generate_union_newtype_trait(
3069    ctx: &GeneratorContext,
3070    node_id: u64,
3071    node_name: &str,
3072) -> ::capnp::Result<FormattedText> {
3073    use capnp::schema_capnp::*;
3074    let Some(template_id) = union_newtype_template_id(ctx, node_id)? else {
3075        return Ok(Branch(vec![]));
3076    };
3077    let node::Struct(tmpl) = ctx.node_map[&template_id].which()? else {
3078        return Ok(Branch(vec![]));
3079    };
3080
3081    let mut variants = Vec::new();
3082    let mut group_params = Vec::new();
3083    let mut reader_assoc = Vec::new();
3084    let mut reader_methods = Vec::new();
3085    let mut builder_assoc = Vec::new();
3086    let mut builder_methods = Vec::new();
3087    let mut needs_lifetime = false;
3088    for field in tmpl.get_fields()? {
3089        let variant = capitalize_first_letter(field.get_name()?.to_str()?);
3090        let name = camel_to_snake_case(field.get_name()?.to_str()?);
3091        if let field::Group(_) = field.which()? {
3092            let nested = ctx.get_qualified_module(field.get_type_id());
3093            variants.push(Line(format!("{variant}({variant}),")));
3094            group_params.push(variant.clone());
3095            reader_assoc.push(Line(format!("type {variant}: {nested}::Reader<'a>;")));
3096            builder_assoc.push(Line(format!("type {variant}: {nested}::Builder<'a>;")));
3097            builder_methods.push(Line(format!("fn init_{name}(self) -> Self::{variant};")));
3098        } else {
3099            let (payload, _, _) = getter_text(ctx, &field, true, false)?;
3100            if let field::Slot(s) = field.which()? {
3101                if s.get_type()?.is_pointer()? {
3102                    needs_lifetime = true;
3103                }
3104            }
3105            variants.push(Line(format!("{variant}({payload}),")));
3106            for m in newtype_trait_methods(ctx, &field, true, false)? {
3107                reader_methods.push(Line(format!("fn {};", m.signature)));
3108            }
3109            for m in newtype_trait_methods(ctx, &field, false, false)? {
3110                builder_methods.push(Line(format!("fn {};", m.signature)));
3111            }
3112        }
3113    }
3114
3115    let which_generics = union_which_generics(needs_lifetime, &group_params);
3116    let self_params: Vec<String> = group_params.iter().map(|p| format!("Self::{p}")).collect();
3117    let which_ret = union_which_generics(needs_lifetime, &self_params);
3118
3119    let mut reader_items = reader_assoc;
3120    reader_items.push(Line(format!(
3121        "fn which(&self) -> ::core::result::Result<Which{which_ret}, ::capnp::NotInSchema>;"
3122    )));
3123    reader_items.extend(reader_methods);
3124    let mut builder_items = builder_assoc;
3125    builder_items.extend(builder_methods);
3126
3127    let mut module_items = vec![
3128        Branch(vec![
3129            Line(format!("pub enum Which{which_generics} {{")),
3130            indent(variants),
3131            line("}"),
3132        ]),
3133        line("pub trait Reader<'a> {"),
3134        indent(reader_items),
3135        line("}"),
3136        line("pub trait Builder<'a> {"),
3137        indent(builder_items),
3138        line("}"),
3139    ];
3140    module_items.extend(union_any_reader_items(ctx, template_id)?);
3141    module_items.extend(union_any_builder_items(ctx, template_id)?);
3142
3143    Ok(Branch(vec![
3144        BlankLine,
3145        Line(format!("pub mod {} {{", module_name(node_name))),
3146        indent(module_items),
3147        line("}"),
3148    ]))
3149}
3150
3151// Impls the union newtype's Reader/Builder traits for a use site. `which()` maps the per-instance
3152// `Which` variants to the shared enum; the arm accessors delegate to the inherent methods (which
3153// carry the right discriminant offset).
3154fn generate_union_newtype_impl(
3155    ctx: &GeneratorContext,
3156    alias_id: u64,
3157    instance_id: u64,
3158) -> ::capnp::Result<FormattedText> {
3159    use capnp::schema_capnp::*;
3160    let Some(template_id) = union_newtype_template_id(ctx, alias_id)? else {
3161        return Ok(Branch(vec![]));
3162    };
3163    let node::Struct(tmpl) = ctx.node_map[&template_id].which()? else {
3164        return Ok(Branch(vec![]));
3165    };
3166    let node::Struct(instance) = ctx.node_map[&instance_id].which()? else {
3167        return Ok(Branch(vec![]));
3168    };
3169
3170    let mut instance_fields = ::std::collections::HashMap::new();
3171    for field in instance.get_fields()? {
3172        instance_fields.insert(field.get_name()?.to_str()?.to_string(), field);
3173    }
3174
3175    let trait_path = ctx.get_qualified_module(alias_id);
3176    let concrete = ctx.get_qualified_module(instance_id);
3177
3178    let mut which_arms = Vec::new();
3179    let mut group_params = Vec::new();
3180    let mut reader_assoc = Vec::new();
3181    let mut reader_methods = Vec::new();
3182    let mut builder_assoc = Vec::new();
3183    let mut builder_methods = Vec::new();
3184    let mut needs_lifetime = false;
3185    for field in tmpl.get_fields()? {
3186        let raw_name = field.get_name()?.to_str()?;
3187        let Some(inst_field) = instance_fields.get(raw_name) else {
3188            return Ok(Branch(vec![]));
3189        };
3190        let variant = capitalize_first_letter(raw_name);
3191        let name = camel_to_snake_case(raw_name);
3192        which_arms.push(Line(format!(
3193            "{concrete}::Which::{variant}(x) => {trait_path}::Which::{variant}(x),"
3194        )));
3195        if let field::Group(_) = field.which()? {
3196            let field::Group(inst_group) = inst_field.which()? else {
3197                return Ok(Branch(vec![]));
3198            };
3199            let inst_mod = ctx.get_qualified_module(inst_group.get_type_id());
3200            group_params.push(variant.clone());
3201            reader_assoc.push(Line(format!("type {variant} = {inst_mod}::Reader<'a>;")));
3202            builder_assoc.push(Line(format!("type {variant} = {inst_mod}::Builder<'a>;")));
3203            builder_methods.push(Line(format!(
3204                "fn init_{name}(self) -> Self::{variant} {{ self.init_{name}() }}"
3205            )));
3206        } else {
3207            if let field::Slot(s) = field.which()? {
3208                if s.get_type()?.is_pointer()? {
3209                    needs_lifetime = true;
3210                }
3211            }
3212            for m in newtype_trait_methods(ctx, &field, true, false)? {
3213                let recv = if m.deref_self { "(*self)" } else { "self" };
3214                reader_methods.push(Line(format!(
3215                    "fn {} {{ {recv}.{}({}) }}",
3216                    m.signature, m.call_name, m.call_args
3217                )));
3218            }
3219            for m in newtype_trait_methods(ctx, &field, false, false)? {
3220                let recv = if m.deref_self { "(*self)" } else { "self" };
3221                builder_methods.push(Line(format!(
3222                    "fn {} {{ {recv}.{}({}) }}",
3223                    m.signature, m.call_name, m.call_args
3224                )));
3225            }
3226        }
3227    }
3228    let self_params: Vec<String> = group_params.iter().map(|p| format!("Self::{p}")).collect();
3229    let which_ret = union_which_generics(needs_lifetime, &self_params);
3230
3231    let which_method = Branch(vec![
3232        Line(format!(
3233            "fn which(&self) -> ::core::result::Result<{trait_path}::Which{which_ret}, ::capnp::NotInSchema> {{"
3234        )),
3235        indent(vec![
3236            line("::core::result::Result::Ok(match (*self).which()? {"),
3237            indent(which_arms),
3238            line("})"),
3239        ]),
3240        line("}"),
3241    ]);
3242
3243    let mut reader_items = reader_assoc;
3244    reader_items.push(which_method);
3245    reader_items.extend(reader_methods);
3246    let mut builder_items = builder_assoc;
3247    builder_items.extend(builder_methods);
3248
3249    // Erased-carrier wiring: the offset table plus a runtime discriminant offset, then `as_any()`.
3250    let mut offsets = Vec::new();
3251    collect_leaf_offsets(ctx, template_id, instance_id, &mut offsets)?;
3252    let n = offsets.len();
3253    let disc = instance.get_discriminant_offset();
3254    let table = format!(
3255        "{}_OFFSETS",
3256        camel_to_snake_case(ctx.get_last_name(instance_id)?).to_ascii_uppercase()
3257    );
3258    let any_items = vec![
3259        BlankLine,
3260        Line(format!(
3261            "static {table}: [u32; {n}] = [{}];",
3262            offsets.join(", ")
3263        )),
3264        Line(format!("impl<'a> {concrete}::Reader<'a> {{")),
3265        indent(Line(fmt!(ctx,
3266            "pub fn as_any(self) -> {trait_path}::AnyReader<'a> {{ {trait_path}::AnyReader::new({capnp}::traits::IntoInternalStructReader::into_internal_struct_reader(self), {disc}, &{table}) }}"
3267        ))),
3268        line("}"),
3269        Line(format!("impl<'a> {concrete}::Builder<'a> {{")),
3270        indent(Line(fmt!(ctx,
3271            "pub fn as_any(self) -> {trait_path}::AnyBuilder<'a> {{ {trait_path}::AnyBuilder::new({capnp}::traits::IntoInternalStructBuilder::into_internal_struct_builder(self), {disc}, &{table}) }}"
3272        ))),
3273        line("}"),
3274    ];
3275
3276    Ok(Branch(vec![
3277        BlankLine,
3278        Line(format!(
3279            "impl<'a> {trait_path}::Reader<'a> for {concrete}::Reader<'a> {{"
3280        )),
3281        indent(reader_items),
3282        line("}"),
3283        BlankLine,
3284        Line(format!(
3285            "impl<'a> {trait_path}::Builder<'a> for {concrete}::Builder<'a> {{"
3286        )),
3287        indent(builder_items),
3288        line("}"),
3289        Branch(any_items),
3290    ]))
3291}
3292
3293// Emits `pub mod <name> { pub type Reader ...; Builder; Owned }` for a scalar newtype (`type Uuid
3294// = Data`, `type Age = UInt16`) so use sites can name the alias. A value newtype's aliases carry
3295// no lifetime; a pointer newtype's Reader/Builder keep `<'a>`. Inline group/union newtypes (which
3296// get a trait instead) and aliases of a pre-existing struct/interface are skipped here.
3297fn generate_scalar_newtype_alias(
3298    ctx: &GeneratorContext,
3299    node_id: u64,
3300    node_name: &str,
3301    t: schema_capnp::type_::Reader,
3302) -> ::capnp::Result<FormattedText> {
3303    use capnp::schema_capnp::*;
3304    if newtype_template_id(ctx, node_id)?.is_some() {
3305        return Ok(Branch(vec![]));
3306    }
3307    if matches!(t.which()?, type_::Struct(_) | type_::Interface(_)) {
3308        return Ok(Branch(vec![]));
3309    }
3310    let module = module_name(node_name);
3311    let items = if t.is_pointer()? {
3312        vec![
3313            Line(format!(
3314                "pub type Reader<'a> = {};",
3315                t.type_string(ctx, Leaf::Reader("'a"))?
3316            )),
3317            Line(format!(
3318                "pub type Builder<'a> = {};",
3319                t.type_string(ctx, Leaf::Builder("'a"))?
3320            )),
3321            Line(format!(
3322                "pub type Owned = {};",
3323                t.type_string(ctx, Leaf::Owned)?
3324            )),
3325        ]
3326    } else {
3327        let v = t.type_string(ctx, Leaf::Reader(""))?;
3328        vec![
3329            Line(format!("pub type Reader = {v};")),
3330            Line(format!("pub type Builder = {v};")),
3331            Line(format!("pub type Owned = {v};")),
3332        ]
3333    };
3334    Ok(Branch(vec![
3335        BlankLine,
3336        Line(format!("pub mod {module} {{")),
3337        indent(items),
3338        line("}"),
3339    ]))
3340}
3341
3342fn generate_node(
3343    ctx: &GeneratorContext,
3344    node_id: u64,
3345    node_name: &str,
3346) -> ::capnp::Result<FormattedText> {
3347    use capnp::schema_capnp::*;
3348
3349    let mut output: Vec<FormattedText> = Vec::new();
3350    let mut nested_output: Vec<FormattedText> = Vec::new();
3351
3352    let node_reader = &ctx.node_map[&node_id];
3353    let nested_nodes = node_reader.get_nested_nodes()?;
3354    for nested_node in nested_nodes {
3355        let id = nested_node.get_id();
3356        nested_output.push(generate_node(ctx, id, ctx.get_last_name(id)?)?);
3357    }
3358
3359    match node_reader.which()? {
3360        node::Type(Ok(t)) => {
3361            output.push(generate_newtype_trait(ctx, node_id, node_name)?);
3362            output.push(generate_scalar_newtype_alias(ctx, node_id, node_name, t)?);
3363        }
3364        node::Type(Err(e)) => {
3365            return Err(Error::failed(format!("error reading node type: {e}")));
3366        }
3367        node::File(()) => {
3368            output.push(Branch(nested_output));
3369        }
3370        node::Struct(struct_reader) => {
3371            let params = node_reader.parameters_texts(ctx);
3372            output.push(BlankLine);
3373
3374            let is_generic = node_reader.get_is_generic();
3375            if is_generic {
3376                output.push(Line(format!(
3377                    "pub mod {} {{ /* {} */",
3378                    node_name,
3379                    params.expanded_list.join(",")
3380                )));
3381            } else {
3382                output.push(Line(format!("pub mod {node_name} {{")));
3383            }
3384            let bracketed_params = if params.params.is_empty() {
3385                "".to_string()
3386            } else {
3387                format!("<{}>", params.params)
3388            };
3389
3390            let mut preamble = Vec::new();
3391            let mut builder_members = Vec::new();
3392            let mut reader_members = Vec::new();
3393            let mut union_fields = Vec::new();
3394            let mut which_enums = Vec::new();
3395            let mut pipeline_impl_interior = Vec::new();
3396            let mut private_mod_interior = Vec::new();
3397
3398            let data_size = struct_reader.get_data_word_count();
3399            let pointer_size = struct_reader.get_pointer_count();
3400            let discriminant_count = struct_reader.get_discriminant_count();
3401            let discriminant_offset = struct_reader.get_discriminant_offset();
3402
3403            private_mod_interior.push(crate::pointer_constants::node_word_array_declaration(
3404                ctx,
3405                "ENCODED_NODE",
3406                *node_reader,
3407                crate::pointer_constants::WordArrayDeclarationOptions { pub_crate: true },
3408            )?);
3409
3410            private_mod_interior.push(generate_get_field_types(ctx, *node_reader)?);
3411            private_mod_interior.push(generate_get_annotation_types(ctx, *node_reader)?);
3412
3413            // `static` instead of `const` so that this has a fixed memory address
3414            // and we can check equality of `RawStructSchema` values by comparing pointers.
3415            private_mod_interior.push(Branch(vec![
3416                Line(fmt!(ctx, "pub(crate) static ARENA: {capnp}::private::arena::GeneratedCodeArena = {capnp}::private::arena::GeneratedCodeArena::new(&ENCODED_NODE);")),
3417                Line(fmt!(ctx,"pub(crate) static RAW_SCHEMA: {capnp}::introspect::RawStructSchema = {capnp}::introspect::RawStructSchema::new(")),
3418                indent(vec![
3419                    Line("&ARENA,".into()),
3420                    Line("NONUNION_MEMBERS,".into()),
3421                    Line("MEMBERS_BY_DISCRIMINANT,".into()),
3422                    Line("MEMBERS_BY_NAME".into()),
3423                ]),
3424                Line(");".into()),
3425            ]));
3426
3427            private_mod_interior.push(generate_members_by_discriminant(*node_reader)?);
3428            private_mod_interior.push(generate_members_by_name(*node_reader)?);
3429
3430            let mut has_pointer_field = false;
3431            let fields = struct_reader.get_fields()?;
3432            for field in fields {
3433                let name = get_field_name(field)?;
3434                let styled_name = camel_to_snake_case(name);
3435
3436                let discriminant_value = field.get_discriminant_value();
3437                let is_union_field = discriminant_value != field::NO_DISCRIMINANT;
3438
3439                match field.which()? {
3440                    field::Slot(s) => match s.get_type()?.which()? {
3441                        type_::Text(())
3442                        | type_::Data(())
3443                        | type_::List(_)
3444                        | type_::Struct(_)
3445                        | type_::Interface(_)
3446                        | type_::AnyPointer(_) => has_pointer_field = true,
3447                        _ => (),
3448                    },
3449                    field::Group(_) => has_pointer_field = true,
3450                }
3451
3452                if !is_union_field {
3453                    pipeline_impl_interior.push(generate_pipeline_getter(ctx, field)?);
3454                    let (ty, get, default_decl) = getter_text(ctx, &field, true, true)?;
3455                    if let Some(default) = default_decl {
3456                        private_mod_interior.push(default.clone());
3457                    }
3458                    reader_members.push(Branch(vec![
3459                        line("#[inline]"),
3460                        Line(format!("pub fn get_{styled_name}(self) {ty} {{")),
3461                        indent(get),
3462                        line("}"),
3463                    ]));
3464
3465                    let (ty_b, get_b, _) = getter_text(ctx, &field, false, true)?;
3466                    builder_members.push(Branch(vec![
3467                        line("#[inline]"),
3468                        Line(format!("pub fn get_{styled_name}(self) {ty_b} {{")),
3469                        indent(get_b),
3470                        line("}"),
3471                    ]));
3472                } else {
3473                    union_fields.push(field);
3474                }
3475
3476                builder_members.push(generate_setter(
3477                    ctx,
3478                    discriminant_offset,
3479                    &styled_name,
3480                    &field,
3481                )?);
3482
3483                reader_members.push(generate_haser(
3484                    discriminant_offset,
3485                    &styled_name,
3486                    &field,
3487                    true,
3488                )?);
3489                builder_members.push(generate_haser(
3490                    discriminant_offset,
3491                    &styled_name,
3492                    &field,
3493                    false,
3494                )?);
3495
3496                if let Ok(field::Group(group)) = field.which() {
3497                    let id = group.get_type_id();
3498                    let text = generate_node(ctx, id, ctx.get_last_name(id)?)?;
3499                    nested_output.push(text);
3500
3501                    // If this group is a stamped inline newtype, give its per-instance module the
3502                    // newtype's semantic trait.
3503                    let alias_id = field.get_type_id();
3504                    if alias_id != 0 {
3505                        nested_output.push(generate_newtype_impl(ctx, alias_id, id)?);
3506                    }
3507                }
3508            }
3509
3510            if discriminant_count > 0 {
3511                let (which_enums1, union_getter, typedef, mut default_decls) =
3512                    generate_union(ctx, discriminant_offset, &union_fields, true, &params)?;
3513                which_enums.push(which_enums1);
3514                which_enums.push(typedef);
3515                reader_members.push(union_getter);
3516
3517                private_mod_interior.append(&mut default_decls);
3518
3519                let (_, union_getter, typedef, _) =
3520                    generate_union(ctx, discriminant_offset, &union_fields, false, &params)?;
3521                which_enums.push(typedef);
3522                builder_members.push(union_getter);
3523
3524                let mut reexports = String::new();
3525                reexports.push_str("pub use self::Which::{");
3526                let mut whichs = Vec::new();
3527                for f in &union_fields {
3528                    whichs.push(capitalize_first_letter(get_field_name(*f)?));
3529                }
3530                reexports.push_str(&whichs.join(","));
3531                reexports.push_str("};");
3532                preamble.push(Line(reexports));
3533                preamble.push(BlankLine);
3534            }
3535
3536            let builder_struct_size =
3537                Branch(vec![
3538                    Line(fmt!(ctx,"impl <{0}> {capnp}::traits::HasStructSize for Builder<'_,{0}> {1} {{",
3539                                 params.params, params.where_clause)),
3540                                 indent(Line(
3541                        fmt!(ctx,"const STRUCT_SIZE: {capnp}::private::layout::StructSize = {capnp}::private::layout::StructSize {{ data: {}, pointers: {} }};", data_size as usize, pointer_size as usize))),
3542                   line("}")]);
3543
3544            private_mod_interior.push(Line(format!(
3545                "pub(crate) const TYPE_ID: u64 = {};",
3546                format_u64(node_id)
3547            )));
3548
3549            let from_pointer_builder_impl =
3550                Branch(vec![
3551                    Line(fmt!(ctx,"impl <'a,{0}> {capnp}::traits::FromPointerBuilder<'a> for Builder<'a,{0}> {1} {{", params.params, params.where_clause)),
3552                    indent(vec![
3553                        Line(fmt!(ctx,"fn init_pointer(builder: {capnp}::private::layout::PointerBuilder<'a>, _size: u32) -> Self {{")),
3554                        indent(Line(fmt!(ctx,"builder.init_struct(<Self as {capnp}::traits::HasStructSize>::STRUCT_SIZE).into()"))),
3555                        line("}"),
3556                        Line(fmt!(ctx,"fn get_from_pointer(builder: {capnp}::private::layout::PointerBuilder<'a>, default: ::core::option::Option<&'a [{capnp}::Word]>) -> {capnp}::Result<Self> {{")),
3557                        indent(Line(fmt!(ctx,"::core::result::Result::Ok(builder.get_struct(<Self as {capnp}::traits::HasStructSize>::STRUCT_SIZE, default)?.into())"))),
3558                        line("}")
3559                    ]),
3560                    line("}"),
3561                    BlankLine]);
3562
3563            let accessors = vec![
3564                Branch(preamble),
3565                (if !is_generic {
3566                    Branch(vec![
3567                        Line("#[derive(Copy, Clone)]".into()),
3568                        line("pub struct Owned(());"),
3569                        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() }} }}")),
3570                        Line(fmt!(ctx, "impl {capnp}::traits::Owned for Owned {{ type Reader<'a> = Reader<'a>; type Builder<'a> = Builder<'a>; }}")),
3571                        Line(fmt!(ctx,"impl {capnp}::traits::OwnedStruct for Owned {{ type Reader<'a> = Reader<'a>; type Builder<'a> = Builder<'a>; }}")),
3572                        Line(fmt!(ctx,"impl {capnp}::traits::Pipelined for Owned {{ type Pipeline = Pipeline; }}"))
3573                    ])
3574                } else {
3575                    Branch(vec![
3576                        Line("#[derive(Copy, Clone)]".into()),
3577                        Line(format!("pub struct Owned<{}> {{", params.params)),
3578                            indent(Line(params.phantom_data_type.clone())),
3579                        line("}"),
3580                        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() }} }}",
3581                            params.params, params.where_clause)),
3582                        Line(fmt!(ctx,"impl <{0}> {capnp}::traits::Owned for Owned <{0}> {1} {{ type Reader<'a> = Reader<'a, {0}>; type Builder<'a> = Builder<'a, {0}>; }}",
3583                            params.params, params.where_clause)),
3584                        Line(fmt!(ctx,"impl <{0}> {capnp}::traits::OwnedStruct for Owned <{0}> {1} {{ type Reader<'a> = Reader<'a, {0}>; type Builder<'a> = Builder<'a, {0}>; }}",
3585                            params.params, params.where_clause)),
3586                        Line(fmt!(ctx,"impl <{0}> {capnp}::traits::Pipelined for Owned<{0}> {1} {{ type Pipeline = Pipeline{2}; }}",
3587                            params.params, params.where_clause, bracketed_params)),
3588                    ])
3589                }),
3590                BlankLine,
3591                (if !is_generic {
3592                    Line(fmt!(ctx,"pub struct Reader<'a> {{ reader: {capnp}::private::layout::StructReader<'a> }}"))
3593                } else {
3594                    Branch(vec![
3595                        Line(format!("pub struct Reader<'a,{}> {} {{", params.params, params.where_clause)),
3596                        indent(vec![
3597                            Line(fmt!(ctx,"reader: {capnp}::private::layout::StructReader<'a>,")),
3598                            Line(params.phantom_data_type.clone()),
3599                        ]),
3600                        line("}")
3601                    ])
3602                }),
3603                // Manually implement Copy/Clone because `derive` only kicks in if all of
3604                // the parameters are known to implement Copy/Clone.
3605                Branch(vec![
3606                    Line(format!("impl <{0}> ::core::marker::Copy for Reader<'_,{0}> {1} {{}}",
3607                                 params.params, params.where_clause)),
3608                    Line(format!("impl <{0}> ::core::clone::Clone for Reader<'_,{0}> {1} {{",
3609                                 params.params, params.where_clause)),
3610                    indent(Line("fn clone(&self) -> Self { *self }".into())),
3611                    Line("}".into())]),
3612                BlankLine,
3613                Branch(vec![
3614                        Line(fmt!(ctx,"impl <{0}> {capnp}::traits::HasTypeId for Reader<'_,{0}> {1} {{",
3615                            params.params, params.where_clause)),
3616                        indent(vec![line("const TYPE_ID: u64 = _private::TYPE_ID;")]),
3617                    line("}")]),
3618                Line(fmt!(ctx,"impl <'a,{0}> ::core::convert::From<{capnp}::private::layout::StructReader<'a>> for Reader<'a,{0}> {1} {{",
3619                            params.params, params.where_clause)),
3620                indent(vec![
3621                    Line(fmt!(ctx,"fn from(reader: {capnp}::private::layout::StructReader<'a>) -> Self {{")),
3622                    indent(Line(format!("Self {{ reader, {} }}", params.phantom_data_value))),
3623                    line("}")
3624                ]),
3625                line("}"),
3626                BlankLine,
3627                Line(fmt!(ctx,"impl <'a,{0}> ::core::convert::From<Reader<'a,{0}>> for {capnp}::dynamic_value::Reader<'a> {1} {{",
3628                            params.params, params.where_clause)),
3629                indent(vec![
3630                    Line(format!("fn from(reader: Reader<'a,{0}>) -> Self {{", params.params)),
3631                    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))),
3632                    line("}")
3633                ]),
3634                line("}"),
3635                BlankLine,
3636                Line(format!("impl <{0}> ::core::fmt::Debug for Reader<'_,{0}> {1} {{",
3637                            params.params, params.where_clause)),
3638                indent(vec![
3639                    Line("fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::result::Result<(), ::core::fmt::Error> {".into()),
3640                    indent(Line(fmt!(ctx,"core::fmt::Debug::fmt(&::core::convert::Into::<{capnp}::dynamic_value::Reader<'_>>::into(*self), f)"))),
3641                    line("}")
3642                ]),
3643                line("}"),
3644
3645                BlankLine,
3646
3647                Line(fmt!(ctx,"impl <'a,{0}> {capnp}::traits::FromPointerReader<'a> for Reader<'a,{0}> {1} {{",
3648                    params.params, params.where_clause)),
3649                indent(vec![
3650                    Line(fmt!(ctx,"fn get_from_pointer(reader: &{capnp}::private::layout::PointerReader<'a>, default: ::core::option::Option<&'a [{capnp}::Word]>) -> {capnp}::Result<Self> {{")),
3651                    indent(line("::core::result::Result::Ok(reader.get_struct(default)?.into())")),
3652                    line("}")
3653                ]),
3654                line("}"),
3655                BlankLine,
3656                Line(fmt!(ctx,"impl <'a,{0}> {capnp}::traits::IntoInternalStructReader<'a> for Reader<'a,{0}> {1} {{",
3657                            params.params, params.where_clause)),
3658                indent(vec![
3659                    Line(fmt!(ctx,"fn into_internal_struct_reader(self) -> {capnp}::private::layout::StructReader<'a> {{")),
3660                    indent(line("self.reader")),
3661                    line("}")
3662                ]),
3663                line("}"),
3664                BlankLine,
3665                Line(fmt!(ctx,"impl <'a,{0}> {capnp}::traits::Imbue<'a> for Reader<'a,{0}> {1} {{",
3666                    params.params, params.where_clause)),
3667                indent(vec![
3668                    Line(fmt!(ctx,"fn imbue(&mut self, cap_table: &'a {capnp}::private::layout::CapTable) {{")),
3669                    indent(Line(fmt!(ctx,"self.reader.imbue({capnp}::private::layout::CapTableReader::from_ref(cap_table))"))),
3670                    line("}")
3671                ]),
3672                line("}"),
3673                BlankLine,
3674                if has_pointer_field { // we do this to keep clippy happy
3675                    Line(format!("impl <'a,{0}> Reader<'a,{0}> {1} {{", params.params, params.where_clause))
3676                } else {
3677                    Line(format!("impl <{0}> Reader<'_,{0}> {1} {{", params.params, params.where_clause))
3678                },
3679                indent(vec![
3680                        Line(format!("pub fn reborrow(&self) -> Reader<'_,{}> {{",params.params)),
3681                        indent(line("Self { .. *self }")),
3682                        line("}"),
3683                        BlankLine,
3684                        Line(fmt!(ctx,"pub fn total_size(&self) -> {capnp}::Result<{capnp}::MessageSize> {{")),
3685                        indent(line("self.reader.total_size()")),
3686                        line("}")]),
3687                indent(reader_members),
3688                line("}"),
3689                BlankLine,
3690                (if !is_generic {
3691                    Line(fmt!(ctx,"pub struct Builder<'a> {{ builder: {capnp}::private::layout::StructBuilder<'a> }}"))
3692                } else {
3693                    Branch(vec![
3694                        Line(format!("pub struct Builder<'a,{}> {} {{",
3695                                     params.params, params.where_clause)),
3696                            indent(vec![
3697                            Line(fmt!(ctx, "builder: {capnp}::private::layout::StructBuilder<'a>,")),
3698                            Line(params.phantom_data_type.clone()),
3699                        ]),
3700                        line("}")
3701                    ])
3702                }),
3703                builder_struct_size,
3704                Branch(vec![
3705                    Line(fmt!(ctx,"impl <{0}> {capnp}::traits::HasTypeId for Builder<'_,{0}> {1} {{",
3706                                 params.params, params.where_clause)),
3707                    indent(vec![
3708                        line("const TYPE_ID: u64 = _private::TYPE_ID;")]),
3709                    line("}")
3710                ]),
3711                Line(fmt!(ctx,
3712                    "impl <'a,{0}> ::core::convert::From<{capnp}::private::layout::StructBuilder<'a>> for Builder<'a,{0}> {1} {{",
3713                    params.params, params.where_clause)),
3714                indent(vec![
3715                        Line(fmt!(ctx,"fn from(builder: {capnp}::private::layout::StructBuilder<'a>) -> Self {{")),
3716                        indent(Line(format!("Self {{ builder, {} }}", params.phantom_data_value))),
3717                        line("}")
3718                ]),
3719                line("}"),
3720                BlankLine,
3721                Line(fmt!(ctx,"impl <'a,{0}> {capnp}::traits::IntoInternalStructBuilder<'a> for Builder<'a,{0}> {1} {{",
3722                            params.params, params.where_clause)),
3723                indent(vec![
3724                    Line(fmt!(ctx,"fn into_internal_struct_builder(self) -> {capnp}::private::layout::StructBuilder<'a> {{")),
3725                    indent(line("self.builder")),
3726                    line("}")
3727                ]),
3728                line("}"),
3729                BlankLine,
3730                Line(fmt!(ctx,"impl <'a,{0}> ::core::convert::From<Builder<'a,{0}>> for {capnp}::dynamic_value::Builder<'a> {1} {{",
3731                            params.params, params.where_clause)),
3732                indent(vec![
3733                        Line(format!("fn from(builder: Builder<'a,{0}>) -> Self {{", params.params)),
3734                        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))),
3735                        line("}")
3736                ]),
3737                line("}"),
3738                BlankLine,
3739
3740                Line(fmt!(ctx,"impl <'a,{0}> {capnp}::traits::ImbueMut<'a> for Builder<'a,{0}> {1} {{",
3741                             params.params, params.where_clause)),
3742                indent(vec![
3743                        Line(fmt!(ctx,"fn imbue_mut(&mut self, cap_table: &'a mut {capnp}::private::layout::CapTable) {{")),
3744                        indent(Line(fmt!(ctx,"self.builder.imbue({capnp}::private::layout::CapTableBuilder::from_ref(cap_table))"))),
3745                        line("}")]),
3746                line("}"),
3747                BlankLine,
3748
3749                from_pointer_builder_impl,
3750                Line(fmt!(ctx,
3751                    "impl <{0}> {capnp}::traits::SetterInput<Owned<{0}>> for Reader<'_,{0}> {1} {{",
3752                    params.params, params.where_clause)),
3753                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) }}"))),
3754                line("}"),
3755                BlankLine,
3756                Line(format!("impl <'a,{0}> Builder<'a,{0}> {1} {{", params.params, params.where_clause)),
3757                indent(vec![
3758                        Line(format!("pub fn into_reader(self) -> Reader<'a,{}> {{", params.params)),
3759                        indent(line("self.builder.into_reader().into()")),
3760                        line("}"),
3761                        Line(format!("pub fn reborrow(&mut self) -> Builder<'_,{}> {{", params.params)),
3762                        (if !is_generic {
3763                            indent(line("Builder { builder: self.builder.reborrow() }"))
3764                        } else {
3765                            indent(line("Builder { builder: self.builder.reborrow(), ..*self }"))
3766                        }),
3767                        line("}"),
3768                        Line(format!("pub fn reborrow_as_reader(&self) -> Reader<'_,{}> {{", params.params)),
3769                        indent(line("self.builder.as_reader().into()")),
3770                        line("}"),
3771
3772                        BlankLine,
3773                        Line(fmt!(ctx,"pub fn total_size(&self) -> {capnp}::Result<{capnp}::MessageSize> {{")),
3774                        indent(line("self.builder.as_reader().total_size()")),
3775                        line("}")
3776                        ]),
3777                indent(builder_members),
3778                line("}"),
3779                BlankLine,
3780                (if is_generic {
3781                    Branch(vec![
3782                        Line(format!("pub struct Pipeline{bracketed_params} {{")),
3783                        indent(vec![
3784                            Line(fmt!(ctx,"_typeless: {capnp}::any_pointer::Pipeline,")),
3785                            Line(params.phantom_data_type),
3786                        ]),
3787                        line("}")
3788                    ])
3789                } else {
3790                    Line(fmt!(ctx,"pub struct Pipeline {{ _typeless: {capnp}::any_pointer::Pipeline }}"))
3791                }),
3792                Line(fmt!(ctx,"impl{bracketed_params} {capnp}::capability::FromTypelessPipeline for Pipeline{bracketed_params} {{")),
3793                indent(vec![
3794                        Line(fmt!(ctx,"fn new(typeless: {capnp}::any_pointer::Pipeline) -> Self {{")),
3795                        indent(Line(format!("Self {{ _typeless: typeless, {} }}", params.phantom_data_value))),
3796                        line("}")]),
3797                line("}"),
3798                Line(format!("impl{0} Pipeline{0} {1} {{", bracketed_params,
3799                             params.pipeline_where_clause)),
3800                indent(pipeline_impl_interior),
3801                line("}"),
3802                line("mod _private {"),
3803                indent(private_mod_interior),
3804                line("}"),
3805            ];
3806
3807            output.push(indent(vec![
3808                Branch(accessors),
3809                Branch(which_enums),
3810                Branch(nested_output),
3811            ]));
3812            output.push(line("}"));
3813        }
3814
3815        node::Enum(enum_reader) => {
3816            let last_name = ctx.get_last_name(node_id)?;
3817            let name_as_mod = module_name(last_name);
3818            output.push(BlankLine);
3819
3820            let mut members = Vec::new();
3821            let mut match_branches = Vec::new();
3822            let enumerants = enum_reader.get_enumerants()?;
3823            for (ii, enumerant) in enumerants.into_iter().enumerate() {
3824                let enumerant = capitalize_first_letter(get_enumerant_name(enumerant)?);
3825                members.push(Line(format!("{enumerant} = {ii},")));
3826                match_branches.push(Line(format!(
3827                    "{ii} => ::core::result::Result::Ok(Self::{enumerant}),"
3828                )));
3829            }
3830            match_branches.push(Line(fmt!(
3831                ctx,
3832                "n => ::core::result::Result::Err({capnp}::NotInSchema(n)),"
3833            )));
3834
3835            output.push(Branch(vec![
3836                line("#[repr(u16)]"),
3837                line("#[derive(Clone, Copy, Debug, PartialEq, Eq)]"),
3838                Line(format!("pub enum {last_name} {{")),
3839                indent(members),
3840                line("}"),
3841            ]));
3842
3843            output.push(BlankLine);
3844            output.push(Branch(vec![
3845                Line(fmt!(ctx,
3846                    "impl {capnp}::introspect::Introspect for {last_name} {{"
3847                )),
3848                indent(Line(fmt!(ctx,
3849                    "fn introspect() -> {capnp}::introspect::Type {{ {capnp}::introspect::TypeVariant::Enum({capnp}::introspect::RawEnumSchema::new(&{0}::ARENA, {0}::get_annotation_types)).into() }}", name_as_mod))),
3850                Line("}".into()),
3851            ]));
3852
3853            output.push(Branch(vec![
3854                Line(fmt!(ctx,"impl ::core::convert::From<{last_name}> for {capnp}::dynamic_value::Reader<'_> {{")),
3855                indent(Line(fmt!(ctx,
3856                    "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 ))),
3857                Line("}".into())
3858            ]));
3859
3860            output.push(Branch(vec![
3861                Line(format!(
3862                    "impl ::core::convert::TryFrom<u16> for {last_name} {{"
3863                )),
3864                indent(Line(
3865                    fmt!(ctx,"type Error = {capnp}::NotInSchema;"),
3866                )),
3867                indent(vec![
3868                    Line(
3869                        format!("fn try_from(value: u16) -> ::core::result::Result<Self, <{last_name} as ::core::convert::TryFrom<u16>>::Error> {{")
3870                    ),
3871                    indent(vec![
3872                        line("match value {"),
3873                        indent(match_branches),
3874                        line("}"),
3875                    ]),
3876                    line("}"),
3877                ]),
3878                line("}"),
3879                Line(format!("impl From<{last_name}> for u16 {{")),
3880                indent(line("#[inline]")),
3881                indent(Line(format!(
3882                    "fn from(x: {last_name}) -> u16 {{ x as u16 }}"
3883                ))),
3884                line("}"),
3885            ]));
3886
3887            output.push(Branch(vec![
3888                Line(fmt!(
3889                    ctx,
3890                    "impl {capnp}::traits::HasTypeId for {last_name} {{"
3891                )),
3892                indent(Line(format!(
3893                    "const TYPE_ID: u64 = {}u64;",
3894                    format_u64(node_id)
3895                ))),
3896                line("}"),
3897            ]));
3898
3899            output.push(Branch(vec![
3900                Line(format!("mod {name_as_mod} {{")),
3901                Branch(vec![
3902                    crate::pointer_constants::node_word_array_declaration(
3903                        ctx,
3904                        "ENCODED_NODE",
3905                        *node_reader,
3906                        crate::pointer_constants::WordArrayDeclarationOptions { pub_crate: true },
3907                    )?,
3908                    generate_get_annotation_types(ctx, *node_reader)?,
3909                    Line(fmt!(ctx, "pub(crate) static ARENA: {capnp}::private::arena::GeneratedCodeArena = {capnp}::private::arena::GeneratedCodeArena::new(&ENCODED_NODE);")),
3910                ]),
3911                Line("}".into()),
3912            ]));
3913        }
3914
3915        node::Interface(interface) => {
3916            let params = node_reader.parameters_texts(ctx);
3917            output.push(BlankLine);
3918
3919            let is_generic = node_reader.get_is_generic();
3920
3921            let names = &ctx.scope_map[&node_id];
3922            let mut client_impl_interior = Vec::new();
3923            let mut server_interior = Vec::new();
3924            let mut mod_interior = Vec::new();
3925            let mut dispatch_arms = Vec::new();
3926            let mut private_mod_interior = Vec::new();
3927
3928            let bracketed_params = if params.params.is_empty() {
3929                "".to_string()
3930            } else {
3931                format!("<{}>", params.params)
3932            };
3933
3934            private_mod_interior.push(Line(format!(
3935                "pub(crate) const TYPE_ID: u64 = {};",
3936                format_u64(node_id)
3937            )));
3938
3939            mod_interior.push(line("#![allow(unused_variables)]"));
3940
3941            let methods = interface.get_methods()?;
3942            for (ordinal, method) in methods.into_iter().enumerate() {
3943                let name = method.get_name()?.to_str()?;
3944
3945                let param_id = method.get_param_struct_type();
3946                let param_node = &ctx.node_map[&param_id];
3947                let (param_scopes, params_ty_params) = if param_node.get_scope_id() == 0 {
3948                    let mut names = names.clone();
3949                    let local_name = module_name(&format!("{name}Params"));
3950                    nested_output.push(generate_node(ctx, param_id, &local_name)?);
3951                    names.push(local_name);
3952                    (names, params.params.clone())
3953                } else {
3954                    (
3955                        ctx.scope_map[&param_node.get_id()].clone(),
3956                        get_ty_params_of_brand(ctx, method.get_param_brand()?)?,
3957                    )
3958                };
3959                let param_type = do_branding(
3960                    ctx,
3961                    param_id,
3962                    method.get_param_brand()?,
3963                    Leaf::Owned,
3964                    &param_scopes.join("::"),
3965                )?;
3966
3967                mod_interior.push(Line(fmt!(
3968                    ctx,
3969                    "pub type {}Params<{}> = {capnp}::capability::Params<{}>;",
3970                    capitalize_first_letter(name),
3971                    params_ty_params,
3972                    param_type
3973                )));
3974
3975                let result_id = method.get_result_struct_type();
3976                if result_id != STREAM_RESULT_ID {
3977                    dispatch_arms.push(
3978                        Line(fmt!(ctx,
3979                                  "{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),",
3980                                  module_name(name))));
3981
3982                    let result_node = &ctx.node_map[&result_id];
3983                    let (result_scopes, results_ty_params) = if result_node.get_scope_id() == 0 {
3984                        let mut names = names.clone();
3985                        let local_name = module_name(&format!("{name}Results"));
3986                        nested_output.push(generate_node(ctx, result_id, &local_name)?);
3987                        names.push(local_name);
3988                        (names, params.params.clone())
3989                    } else {
3990                        (
3991                            ctx.scope_map[&result_node.get_id()].clone(),
3992                            get_ty_params_of_brand(ctx, method.get_result_brand()?)?,
3993                        )
3994                    };
3995                    let result_type = do_branding(
3996                        ctx,
3997                        result_id,
3998                        method.get_result_brand()?,
3999                        Leaf::Owned,
4000                        &result_scopes.join("::"),
4001                    )?;
4002                    mod_interior.push(Line(fmt!(
4003                        ctx,
4004                        "pub type {}Results<{}> = {capnp}::capability::Results<{}>;",
4005                        capitalize_first_letter(name),
4006                        results_ty_params,
4007                        result_type
4008                    )));
4009                    server_interior.push(
4010                        Line(fmt!(ctx,
4011                                  "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()))) }}",
4012                                  module_name(name),
4013                                  capitalize_first_letter(name), params_ty_params,
4014                                  capitalize_first_letter(name), results_ty_params,
4015                                  node_name, module_name(name)
4016                        )));
4017
4018                    client_impl_interior.push(Line(fmt!(
4019                        ctx,
4020                        "pub fn {}_request(&self) -> {capnp}::capability::Request<{},{}> {{",
4021                        camel_to_snake_case(name),
4022                        param_type,
4023                        result_type
4024                    )));
4025
4026                    client_impl_interior.push(indent(Line(format!(
4027                        "self.client.new_call(_private::TYPE_ID, {ordinal}, ::core::option::Option::None)"
4028                    ))));
4029                    client_impl_interior.push(line("}"));
4030                } else {
4031                    // It's a streaming method.
4032                    dispatch_arms.push(
4033                        Line(fmt!(ctx,
4034                                  "{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),",
4035
4036                                  module_name(name))));
4037
4038                    server_interior.push(
4039                        Line(fmt!(ctx,
4040                                  "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()))) }}",
4041                                  module_name(name),
4042                                  capitalize_first_letter(name), params_ty_params,
4043                                  node_name, module_name(name)
4044                        )));
4045                    client_impl_interior.push(Line(fmt!(
4046                        ctx,
4047                        "pub fn {}_request(&self) -> {capnp}::capability::StreamingRequest<{}> {{",
4048                        camel_to_snake_case(name),
4049                        param_type
4050                    )));
4051                    client_impl_interior.push(indent(Line(format!(
4052                        "self.client.new_streaming_call(_private::TYPE_ID, {ordinal}, ::core::option::Option::None)"
4053                    ))));
4054
4055                    client_impl_interior.push(line("}"));
4056                }
4057
4058                method.get_annotations()?;
4059            }
4060
4061            let mut base_dispatch_arms = Vec::new();
4062
4063            let server_base = {
4064                let mut base_traits = Vec::new();
4065
4066                fn find_super_interfaces<'a>(
4067                    interface: schema_capnp::node::interface::Reader<'a>,
4068                    all_extends: &mut Vec<
4069                        <schema_capnp::superclass::Owned as capnp::traits::OwnedStruct>::Reader<'a>,
4070                    >,
4071                    ctx: &GeneratorContext<'a>,
4072                ) -> ::capnp::Result<()> {
4073                    let extends = interface.get_superclasses()?;
4074                    for superclass in extends {
4075                        if let node::Interface(interface) =
4076                            ctx.node_map[&superclass.get_id()].which()?
4077                        {
4078                            find_super_interfaces(interface, all_extends, ctx)?;
4079                        }
4080                        all_extends.push(superclass);
4081                    }
4082                    Ok(())
4083                }
4084
4085                let mut extends = Vec::new();
4086                find_super_interfaces(interface, &mut extends, ctx)?;
4087                for interface in &extends {
4088                    let type_id = interface.get_id();
4089                    let brand = interface.get_brand()?;
4090                    let the_mod = ctx.get_qualified_module(type_id);
4091
4092                    base_dispatch_arms.push(Line(format!(
4093                        "0x{type_id:x} => {}::dispatch_call_internal(self.server, method_id, params, results),",
4094                        do_branding(
4095                            ctx, type_id, brand, Leaf::ServerDispatch, &the_mod)?)));
4096                    base_traits.push(do_branding(ctx, type_id, brand, Leaf::Server, &the_mod)?);
4097                }
4098
4099                // Defining that the server itself should always be 'static makes
4100                // bounds easier down the line.
4101                if !extends.is_empty() {
4102                    format!(": {} + 'static", base_traits.join(" + "))
4103                } else {
4104                    ": 'static".to_string()
4105                }
4106            };
4107
4108            mod_interior.push(BlankLine);
4109            mod_interior.push(Line(format!("pub struct Client{bracketed_params} {{")));
4110            mod_interior.push(indent(Line(fmt!(
4111                ctx,
4112                "pub client: {capnp}::capability::Client,"
4113            ))));
4114            if is_generic {
4115                mod_interior.push(indent(Line(params.phantom_data_type.clone())));
4116            }
4117            mod_interior.push(line("}"));
4118            mod_interior.push(
4119                Branch(vec![
4120                    Line(fmt!(ctx,"impl {bracketed_params} {capnp}::capability::FromClientHook for Client{bracketed_params} {{")),
4121                    indent(Line(fmt!(ctx,"fn new(hook: Box<{capnp}::capability::DynClientHook>) -> Self {{"))),
4122                    indent(indent(Line(fmt!(ctx,"Self {{ client: {capnp}::capability::Client::new(hook), {} }}", params.phantom_data_value)))),
4123                    indent(line("}")),
4124                    indent(Line(fmt!(ctx,"fn into_client_hook(self) -> Box<{capnp}::capability::DynClientHook> {{"))),
4125                    indent(indent(line("self.client.hook"))),
4126                    indent(line("}")),
4127                    indent(Line(fmt!(ctx,"fn as_client_hook(&self) -> &{capnp}::capability::DynClientHook {{"))),
4128                    indent(indent(line("&*self.client.hook"))),
4129                    indent(line("}")),
4130                    line("}")]));
4131
4132            mod_interior.push(if !is_generic {
4133                Branch(vec![
4134                    Line("#[derive(Copy, Clone)]".into()),
4135                    line("pub struct Owned(());"),
4136                    Line(fmt!(ctx,"impl {capnp}::introspect::Introspect for Owned {{ fn introspect() -> {capnp}::introspect::Type {{ {capnp}::introspect::TypeVariant::Capability.into() }} }}")),
4137                    Line(fmt!(ctx,"impl {capnp}::traits::Owned for Owned {{ type Reader<'a> = Client; type Builder<'a> = Client; }}")),
4138                    Line(fmt!(ctx,"impl {capnp}::traits::Pipelined for Owned {{ type Pipeline = Client; }}"))])
4139            } else {
4140                Branch(vec![
4141                    Line("#[derive(Copy, Clone)]".into()),
4142                    Line(format!("pub struct Owned<{}> {} {{", params.params, params.where_clause)),
4143                    indent(Line(params.phantom_data_type.clone())),
4144                    line("}"),
4145                    Line(fmt!(ctx,
4146                              "impl <{0}> {capnp}::introspect::Introspect for Owned <{0}> {1} {{ fn introspect() -> {capnp}::introspect::Type {{ {capnp}::introspect::TypeVariant::Capability.into() }} }}",
4147                              params.params, params.where_clause)),
4148                    Line(fmt!(ctx,
4149                        "impl <{0}> {capnp}::traits::Owned for Owned <{0}> {1} {{ type Reader<'a> = Client<{0}>; type Builder<'a> = Client<{0}>; }}",
4150                        params.params, params.where_clause)),
4151                    Line(fmt!(ctx,
4152                        "impl <{0}> {capnp}::traits::Pipelined for Owned <{0}> {1} {{ type Pipeline = Client{2}; }}",
4153                        params.params, params.where_clause, bracketed_params))])
4154            });
4155
4156            mod_interior.push(Branch(vec![
4157                Line(fmt!(ctx,"impl <'a,{0}> {capnp}::traits::FromPointerReader<'a> for Client<{0}> {1} {{",
4158                    params.params, params.where_clause)),
4159                indent(vec![
4160                        Line(fmt!(ctx,"fn get_from_pointer(reader: &{capnp}::private::layout::PointerReader<'a>, _default: ::core::option::Option<&'a [{capnp}::Word]>) -> {capnp}::Result<Self> {{")),
4161                        indent(Line(fmt!(ctx,"::core::result::Result::Ok({capnp}::capability::FromClientHook::new(reader.get_capability()?))"))),
4162                        line("}")]),
4163                line("}")]));
4164
4165            mod_interior.push(Branch(vec![
4166                Line(fmt!(ctx,"impl <'a,{0}> {capnp}::traits::FromPointerBuilder<'a> for Client<{0}> {1} {{",
4167                             params.params, params.where_clause)),
4168                indent(vec![
4169                            Line(fmt!(ctx,"fn init_pointer(_builder: {capnp}::private::layout::PointerBuilder<'a>, _size: u32) -> Self {{")),
4170                            indent(line("unimplemented!()")),
4171                            line("}"),
4172                            Line(fmt!(ctx,"fn get_from_pointer(builder: {capnp}::private::layout::PointerBuilder<'a>, _default: ::core::option::Option<&'a [{capnp}::Word]>) -> {capnp}::Result<Self> {{")),
4173                            indent(Line(fmt!(ctx,"::core::result::Result::Ok({capnp}::capability::FromClientHook::new(builder.get_capability()?))"))),
4174                            line("}")]),
4175                line("}"),
4176                BlankLine]));
4177
4178            mod_interior.push(Branch(vec![
4179                Line(fmt!(ctx,
4180                    "impl <{0}> {capnp}::traits::SetterInput<Owned<{0}>> for Client<{0}> {1} {{",
4181                    params.params, params.where_clause)),
4182                indent(vec![
4183                            Line(fmt!(ctx,"fn set_pointer_builder(mut pointer: {capnp}::private::layout::PointerBuilder<'_>, from: Self, _canonicalize: bool) -> {capnp}::Result<()> {{")),
4184                            indent(Line("pointer.set_capability(from.client.hook);".to_string())),
4185                            indent(Line("::core::result::Result::Ok(())".to_string())),
4186                            line("}")
4187                        ]
4188                ),
4189                line("}")]));
4190
4191            mod_interior.push(Branch(vec![
4192                Line(fmt!(ctx,
4193                    "impl {bracketed_params} {capnp}::traits::HasTypeId for Client{bracketed_params} {{"
4194                )),
4195                indent(Line(
4196                    "const TYPE_ID: u64 = _private::TYPE_ID;".to_string(),
4197                )),
4198                line("}"),
4199            ]));
4200
4201            mod_interior.push(Branch(vec![
4202                Line(format!(
4203                    "impl {bracketed_params} Clone for Client{bracketed_params} {{"
4204                )),
4205                indent(line("fn clone(&self) -> Self {")),
4206                indent(indent(Line(format!(
4207                    "Self {{ client: self.client.clone(), {} }}",
4208                    params.phantom_data_value
4209                )))),
4210                indent(line("}")),
4211                line("}"),
4212            ]));
4213
4214            mod_interior.push(Branch(vec![
4215                Line(format!(
4216                    "impl {bracketed_params} Client{bracketed_params} {{"
4217                )),
4218                indent(client_impl_interior),
4219                line("}"),
4220            ]));
4221
4222            mod_interior.push(Branch(vec![
4223                Line(format!(
4224                    "pub trait Server<{}> {} {} {{",
4225                    params.params, server_base, params.where_clause
4226                )),
4227                indent(server_interior),
4228                line("}"),
4229            ]));
4230
4231            mod_interior.push(Branch(vec![
4232                Line(format!(
4233                    "pub struct ServerDispatch<_T,{}> {{",
4234                    params.params
4235                )),
4236                indent(line(fmt!(ctx, "pub server: {capnp}::capability::Rc<_T>,"))),
4237                indent(if is_generic {
4238                    vec![Line(params.phantom_data_type.clone())]
4239                } else {
4240                    vec![]
4241                }),
4242                line("}"),
4243            ]));
4244
4245            mod_interior.push(Branch(vec![
4246                Line(
4247                    fmt!(ctx,"impl <_S: Server{1} + 'static, {0}> {capnp}::capability::FromServer<_S> for Client{1} {2}  {{",
4248                            params.params, bracketed_params, params.where_clause)),
4249                indent(vec![
4250                    Line(format!("type Dispatch = ServerDispatch<_S, {}>;", params.params)),
4251                    Line(fmt!(ctx, "fn from_server(s: {capnp}::capability::Rc<_S>) -> ServerDispatch<_S, {}> {{", params.params)),
4252                    indent(Line(format!("ServerDispatch {{ server: s, {} }}", params.phantom_data_value))),
4253                    line("}"),
4254                ]),
4255                line("}"),
4256            ]));
4257
4258            mod_interior.push(
4259                Branch(vec![
4260                    (if is_generic {
4261                        Line(format!("impl <{}, _T: Server{}> ::core::ops::Deref for ServerDispatch<_T,{}> {} {{", params.params, bracketed_params, params.params, params.where_clause))
4262                    } else {
4263                        line("impl <_T: Server> ::core::ops::Deref for ServerDispatch<_T> {")
4264                    }),
4265                    indent(line("type Target = _T;")),
4266                    indent(line("fn deref(&self) -> &_T { &self.server}")),
4267                    line("}"),
4268                    ]));
4269
4270            mod_interior.push(
4271                Branch(vec![
4272                    (if is_generic {
4273                        Line(format!("impl <{}, _T: Server{}> ::core::clone::Clone for ServerDispatch<_T,{}> {} {{", params.params, bracketed_params, params.params, params.where_clause))
4274                    } else {
4275                        line("impl <_T: Server> ::core::clone::Clone for ServerDispatch<_T> {")
4276                    }),
4277                    indent(line(
4278                        format!("fn clone(&self) -> Self {{ Self {{ server: self.server.clone(), {} }} }}", params.phantom_data_value))),
4279                    line("}"),
4280                    ]));
4281
4282            mod_interior.push(
4283                Branch(vec![
4284                    (if is_generic {
4285                        Line(fmt!(ctx,"impl <{}, _T: Server{}> {capnp}::capability::Server for ServerDispatch<_T,{}> {} {{", params.params, bracketed_params, params.params, params.where_clause))
4286                    } else {
4287                        Line(fmt!(ctx,"impl <_T: Server> {capnp}::capability::Server for ServerDispatch<_T> {{"))
4288                    }),
4289                    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 {{"))),
4290                    indent(indent(line("match interface_id {"))),
4291                    indent(indent(indent(line("_private::TYPE_ID => Self::dispatch_call_internal(self.server, method_id, params, results),")))),
4292                    indent(indent(indent(base_dispatch_arms))),
4293                    indent(indent(indent(Line(fmt!(ctx,"_ => {{ {capnp}::capability::DispatchCallResult::new({capnp}::capability::Promise::err({capnp}::Error::unimplemented(\"Method not implemented.\".to_string())), false) }}"))))),
4294                    indent(indent(line("}"))),
4295                    indent(line("}")),
4296
4297                    indent(Line(fmt!(ctx, "fn as_ptr(&self) -> usize {{ {capnp}::capability::Rc::as_ptr(&self.server) as usize }}"))),
4298
4299                    line("}")]));
4300
4301            mod_interior.push(
4302                Branch(vec![
4303                    (if is_generic {
4304                        Line(format!("impl <{}, _T: Server{}> ServerDispatch<_T,{}> {} {{", params.params, bracketed_params, params.params, params.where_clause))
4305                    } else {
4306                        line("impl <_T :Server> ServerDispatch<_T> {")
4307                    }),
4308
4309                    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 {{"))),
4310                    indent(indent(line("match method_id {"))),
4311                    indent(indent(indent(dispatch_arms))),
4312                    indent(indent(indent(Line(fmt!(ctx,"_ => {{ {capnp}::capability::DispatchCallResult::new({capnp}::capability::Promise::err({capnp}::Error::unimplemented(\"Method not implemented.\".to_string())), false) }}"))))),
4313                    indent(indent(line("}"))),
4314                    indent(line("}")),
4315                    line("}")]));
4316
4317            mod_interior.push(Branch(vec![
4318                line("pub(crate) mod _private {"),
4319                indent(private_mod_interior),
4320                line("}"),
4321            ]));
4322
4323            mod_interior.push(Branch(vec![Branch(nested_output)]));
4324
4325            output.push(BlankLine);
4326            if is_generic {
4327                output.push(Line(format!(
4328                    "pub mod {} {{ /* ({}) */",
4329                    node_name,
4330                    params.expanded_list.join(",")
4331                )));
4332            } else {
4333                output.push(Line(format!("pub mod {node_name} {{")));
4334            }
4335            output.push(indent(mod_interior));
4336            output.push(line("}"));
4337        }
4338
4339        node::Const(c) => {
4340            let styled_name = ctx.get_last_name(node_id)?.to_ascii_uppercase();
4341
4342            let typ = c.get_type()?;
4343            let formatted_text = match (typ.which()?, c.get_value()?.which()?) {
4344                (type_::Void(()), value::Void(())) => {
4345                    Line(format!("pub const {styled_name}: () = ();"))
4346                }
4347                (type_::Bool(()), value::Bool(b)) => {
4348                    Line(format!("pub const {styled_name}: bool = {b};"))
4349                }
4350                (type_::Int8(()), value::Int8(i)) => {
4351                    Line(format!("pub const {styled_name}: i8 = {i};"))
4352                }
4353                (type_::Int16(()), value::Int16(i)) => {
4354                    Line(format!("pub const {styled_name}: i16 = {i};"))
4355                }
4356                (type_::Int32(()), value::Int32(i)) => {
4357                    Line(format!("pub const {styled_name}: i32 = {i};"))
4358                }
4359                (type_::Int64(()), value::Int64(i)) => {
4360                    Line(format!("pub const {styled_name}: i64 = {i};"))
4361                }
4362                (type_::Uint8(()), value::Uint8(i)) => {
4363                    Line(format!("pub const {styled_name}: u8 = {i};"))
4364                }
4365                (type_::Uint16(()), value::Uint16(i)) => {
4366                    Line(format!("pub const {styled_name}: u16 = {i};"))
4367                }
4368                (type_::Uint32(()), value::Uint32(i)) => {
4369                    Line(format!("pub const {styled_name}: u32 = {i};"))
4370                }
4371                (type_::Uint64(()), value::Uint64(i)) => {
4372                    Line(format!("pub const {styled_name}: u64 = {i};"))
4373                }
4374
4375                (type_::Float32(()), value::Float32(f)) => {
4376                    let literal = match f.classify() {
4377                        std::num::FpCategory::Nan => "f32::NAN".into(),
4378                        std::num::FpCategory::Infinite => {
4379                            if f.is_sign_positive() {
4380                                "f32::INFINITY".into()
4381                            } else {
4382                                "f32::NEG_INFINITY".into()
4383                            }
4384                        }
4385                        _ => format!("{f:e}"),
4386                    };
4387                    Line(format!("pub const {styled_name}: f32 = {literal};"))
4388                }
4389
4390                (type_::Float64(()), value::Float64(f)) => {
4391                    let literal = match f.classify() {
4392                        std::num::FpCategory::Nan => "f64::NAN".into(),
4393                        std::num::FpCategory::Infinite => {
4394                            if f.is_sign_positive() {
4395                                "f64::INFINITY".into()
4396                            } else {
4397                                "f64::NEG_INFINITY".into()
4398                            }
4399                        }
4400                        _ => format!("{f:e}"),
4401                    };
4402                    Line(format!("pub const {styled_name}: f64 = {literal};"))
4403                }
4404
4405                (type_::Enum(e), value::Enum(v)) => {
4406                    if let Some(node) = ctx.node_map.get(&e.get_type_id()) {
4407                        match node.which()? {
4408                            node::Enum(e) => {
4409                                let enumerants = e.get_enumerants()?;
4410                                if let Some(enumerant) = enumerants.try_get(u32::from(v)) {
4411                                    let variant =
4412                                        capitalize_first_letter(get_enumerant_name(enumerant)?);
4413                                    let type_string = typ.type_string(ctx, Leaf::Owned)?;
4414                                    Line(format!(
4415                                        "pub const {}: {} = {}::{};",
4416                                        styled_name, type_string, type_string, variant
4417                                    ))
4418                                } else {
4419                                    return Err(Error::failed(format!(
4420                                        "enumerant out of range: {v}"
4421                                    )));
4422                                }
4423                            }
4424                            _ => {
4425                                return Err(Error::failed(format!(
4426                                    "bad enum type ID: {}",
4427                                    e.get_type_id()
4428                                )));
4429                            }
4430                        }
4431                    } else {
4432                        return Err(Error::failed(format!(
4433                            "bad enum type ID: {}",
4434                            e.get_type_id()
4435                        )));
4436                    }
4437                }
4438
4439                (type_::Text(()), value::Text(t)) => Line(format!(
4440                    "pub const {styled_name}: &str = {:?};",
4441                    t?.to_str()?
4442                )),
4443                (type_::Data(()), value::Data(d)) => {
4444                    Line(format!("pub const {styled_name}: &[u8] = &{:?};", d?))
4445                }
4446
4447                (type_::List(_), value::List(v)) => {
4448                    generate_pointer_constant(ctx, &styled_name, typ, v)?
4449                }
4450                (type_::Struct(_), value::Struct(v)) => {
4451                    generate_pointer_constant(ctx, &styled_name, typ, v)?
4452                }
4453
4454                (type_::Interface(_t), value::Interface(())) => {
4455                    return Err(Error::unimplemented("interface constants".to_string()));
4456                }
4457                (type_::AnyPointer(_), value::AnyPointer(_pr)) => {
4458                    return Err(Error::unimplemented("anypointer constants".to_string()));
4459                }
4460
4461                _ => {
4462                    return Err(Error::failed("type does not match value".to_string()));
4463                }
4464            };
4465
4466            output.push(formatted_text);
4467        }
4468
4469        node::Annotation(annotation_reader) => {
4470            let is_generic = node_reader.get_is_generic();
4471            let params = node_reader.parameters_texts(ctx);
4472            let last_name = ctx.get_last_name(node_id)?;
4473            let mut interior = vec![];
4474            interior.push(Line(format!("pub const ID: u64 = 0x{node_id:x};")));
4475
4476            let ty = annotation_reader.get_type()?;
4477            if !is_generic {
4478                interior.push(Line(fmt!(ctx,
4479                    "pub fn get_type() -> {capnp}::introspect::Type {{ <{} as {capnp}::introspect::Introspect>::introspect() }}", ty.type_string(ctx, Leaf::Owned)?)));
4480            } else {
4481                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)?)));
4482            }
4483            output.push(Branch(vec![
4484                Line(format!("pub mod {last_name} {{")),
4485                indent(interior),
4486                Line("}".into()),
4487            ]));
4488        }
4489    }
4490
4491    Ok(Branch(output))
4492}
4493
4494// TODO: make indent take Into<FormattedText>, impl Into<FormattedText> for vec (branch)