capnpc-0.19.0/.cargo_vcs_info.json0000644000000001440000000000100123660ustar { "git": { "sha1": "4e1a30a1ea658d7c8f5bf9a91238fe7953058da4" }, "path_in_vcs": "capnpc" }capnpc-0.19.0/CHANGELOG.md000064400000000000000000000136131046102023000127740ustar 00000000000000## v0.19.0 - Include new members_by_name field of RawStructSchema. - Generalize text, primitive_list, and enum_list setters using impl SetterInput. ## v0.18.1 - Fix overly-restrictive lifetimes in setters of certain list fields. ## v0.18.0 - Update for lazier utf-8 validation. ## v0.17.2 - Add the `$Rust.option` annotation for declaring fields to be optional. - Add `CompilerCommand::crate_provides()`, allowing cross-crate imports. ## v0.17.1 - Fix setters of enum fields with defaults. ## v0.17.0 - Add support for reflection. - Implement `Debug` for all generated struct `Reader` types. ## v0.16.5 - Use `core::marker` instead of `std::marker` for pointer constants, for no_std compat. ## v0.16.4 - Generate explicit Clone and Copy impls for Reader structs. - Fully-qualify `::capnp::Word` in generated code. - Add `capnp --version` invocation before `capnp compile`, for better error reporting. - Clear PWD env variable, to silence warning from kj/filesystem-disk-unix.c++. ## v0.16.3 - Generate `*_has()` methods for capability fields. ## v0.16.2 - Avoid ambiguous associated item in TryFrom implementations. ## v0.16.1 - Fix clippy warnings in generated code. ## v0.16.0 - Update code generation for removal of `To16`, `FromU16`, `FromStructReader`, `FromStructBuilder`. ## v0.15.2 - Apply clippy lifetime elision suggestion in set_pointer_builder() in generated code. ## v0.15.1 - Lots of style fixes and linting, including for generated code. ## v0.15.0 - Support trait changes in capnp::traits. - Remove deprecated function. ## v0.14.9 - Fix Clippy warnings in generated code. ## v0.14.8 - Include name of method in `unimplemented` error messages. - Fix super interface lookup in case of transitive `extends` chain. ## v0.14.7 - Canonicalize order of type parameters from the bugfix for issue 260. ## v0.14.6 - Fix bug in code generation for unions that don't use all of their enclosing struct's generic params. ## v0.14.5 - Fix bug in code generation for generic groups. - Add CompilerCommand.raw_code_generator_request_path(). ## v0.14.4 - Check that schema files exist before attempting to invoke the schema compiler. ## v0.14.3 - Include LICENSE in published crate. ## v0.14.2 - Add CompilerCommand::default_parent_module() option. - Add codegen::CodeGenerationCommand and deprecate codegen::generate_code(). ## v0.14.1 - Get generated code to pass the elided_lifetimes_in_paths lint. ## v0.14.0 - Update for `SetPointerBuilder` no longer having a `To` type parameter. - Make generated `Owned` structs unconstructable. They are only intented to be used as types, not values. ## v0.13.1 - Fix some more clippy warnings in generated code. ## v0.13.0 - Update to work without requiring "std" feature of capnp base crate. - Refer to `core` instead of `std` in generated code. - Remove deprecated `ToClient` structs from generated code. ## v0.12.4 - Add `CompilerCommand.capnp_executable()`. - Remove obsolete `RustEdition` enum. ## v0.12.3 - Generate code for new capnp::capability::FromServer trait. ## v0.12.2 - Add `parentModule` annotation to allow generated code to be included in a submodule. ## v0.12.1 - Add rust.capnp file with `name` annotation for renaming items in generated Rust code. ## v0.12.0 - Remove deprecated item. ## v0.11.1 - Avoid generating some superfluous parentheses. ## v0.11.0 - Remove unused experimental `schema` module. - Bump minimum supported rustc version to 1.39.0. ## v0.10.2 - Include the string "@generated" generated code. - Don't write output files if their content is unchanged. ## v0.10.1 - Allow CompilerCommand to work even when OUT_DIR is not set. ## v0.10.0 - Simplify handling of pointer defaults. - Use new const fn ::capnp::word() instead of capnp_word!() macro. - Remove deprecated items. - Use dyn keyword for trait objects. - Deprecate edition() configuration. Apparently the code we generate for Rust 2018 also works for Rust 2015 now. - Update to 2018 edition. - Update minimum required rustc version to 1.35. ## v0.9.5 - Fix bug in code generation for generic interfaces. ## v0.9.4 - Add support for pointer field defaults. ## v0.9.3 - Generate impls of new IntoInternalStructReader trait, to support set_with_caveats. - Update deprecation attributes, to satisfy clippy. ## v0.9.2 - Rename a bunch of as_reader() methods to into_reader(), to satisfy clippy. ## v0.9.1 - Add support for Rust 2018. - Fix a bunch of clippy warnings. ## v0.9.0 - Remove deprecated items. ## v0.8.9 - Deprecate borrow() in favor of reborrow(). ## v0.8.8 - Support List(Interface). ## v0.8.7 - Eliminate `use` statements in generated code to avoid naming conflicts. ## v0.8.6 - Improve error message for snake_case method names. - Eliminate floating point literals in match statements. ## v0.8.5 - Implement enum defaults. - Emit "UNIMPLEMENTED" warnings on struct and list defaults. ## v0.8.4 - Implement struct, list, and enum constants. ## v0.8.3 - Fix bug where schemas with non-trivial relative filesystem paths could fail to compile. ## v0.8.2 - Fix bug where `initn_*()` methods of generic unions failed to set the discriminant. ## v0.8.1 - Fix several formatting issues in generated code. - Remove some unneccesary trait bounds in generated code. - Add `import_path()` and `no_std_import()` options to `CompilerCommand`. ## v0.8.0 - Remove deprecated `compile()` and `compile_with_src_prefixes()` functions. ## v0.7.5 - Fix bug that prevented compilation of interfaces with generic superclasses. - More robust error handling. ## v0.7.4 - Deprecate `compile()` and `compile_with_src_prefixes()` in favor of `CompilerCommand`. ## v0.7.3 - `capnpc -orust ./foo/bar/baz.capnp` now correctly writes to `./foo/bar/baz_capnp.rs` rather than just `./baz_capnp.rs`. If you were depending on the old behavior you can use the `--src-prefix` flag for finer-grained control of the output location. ## v0.7.2 - Nicer formatting for floating point literals. ## v0.7.1 - Fix bug that prevented pipelining on an AnyPointer field. capnpc-0.19.0/Cargo.lock0000644000000011300000000000100103350ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "capnp" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5555adc392d19e38339531d9751cda671492a6346e65ed3f6113e9d0b0eb4ab3" dependencies = [ "embedded-io", ] [[package]] name = "capnpc" version = "0.19.0" dependencies = [ "capnp", ] [[package]] name = "embedded-io" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" capnpc-0.19.0/Cargo.toml0000644000000017670000000000100104000ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "capnpc" version = "0.19.0" authors = ["David Renshaw "] description = "Cap'n Proto code generation" documentation = "https://docs.rs/capnpc/" readme = "README.md" keywords = [ "encoding", "protocol", "serialization", ] license = "MIT" repository = "https://github.com/capnproto/capnproto-rust" [[bin]] name = "capnpc-rust" path = "src/capnpc-rust.rs" [[bin]] name = "capnpc-rust-bootstrap" path = "src/capnpc-rust-bootstrap.rs" [dependencies.capnp] version = "0.19.0" [lints] capnpc-0.19.0/Cargo.toml.orig000064400000000000000000000011001046102023000140360ustar 00000000000000[package] name = "capnpc" version = "0.19.0" authors = [ "David Renshaw " ] license = "MIT" description = "Cap'n Proto code generation" repository = "https://github.com/capnproto/capnproto-rust" documentation = "https://docs.rs/capnpc/" edition = "2021" readme = "README.md" keywords = ["encoding", "protocol", "serialization"] [[bin]] name = "capnpc-rust" path = "src/capnpc-rust.rs" [[bin]] name = "capnpc-rust-bootstrap" path = "src/capnpc-rust-bootstrap.rs" [dependencies.capnp] version = "0.19.0" path = "../capnp" [lints] workspace = true capnpc-0.19.0/LICENSE000064400000000000000000000021121046102023000121600ustar 00000000000000Copyright (c) 2013-2018 Sandstorm Development Group, Inc. and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.capnpc-0.19.0/README.md000064400000000000000000000006021046102023000124340ustar 00000000000000# Cap'n Proto code generation for Rust [![crates.io](https://img.shields.io/crates/v/capnpc.svg)](https://crates.io/crates/capnpc) [documentation](https://docs.rs/capnpc/) The generated code depends on the [capnproto-rust runtime library](https://github.com/capnproto/capnproto-rust). Code generation can be customized through the annotations defined in [`rust.capnp`](rust.capnp). capnpc-0.19.0/rust.capnp000064400000000000000000000036201046102023000132000ustar 00000000000000# This file contains annotations that are recognized by the capnpc-rust code generator. # # To use this file, you will need to make sure that it is included in the directories # searched by `capnp compile`. An easy way to do that is to copy it into your project # alongside your own schema files. @0x83b3c14c3c8dd083; annotation name @0xc2fe4c6d100166d0 (field, struct, enum, enumerant, union, group) :Text; # Rename something in the generated code. The value that you specify in this # annotation should follow capnp capitalization conventions. So, for example, # a struct should use CamelCase capitalization like `StructFoo`, even though # that will get translated to a `struct_foo` module in the generated Rust code. # # TODO: support annotating more kinds of things with this. annotation parentModule @0xabee386cd1450364 (file) :Text; # A Rust module path indicating where the generated code will be included. # For example, if this is set to "foo::bar" and the schema file is named # "baz.capnp", then you could include the generated code like this: # # pub mod foo { # pub mod bar { # pub mod baz_capnp { # include!(concat!(env!("OUT_DIR"), "/baz_capnp.rs")); # } # } # } annotation option @0xabfef22c4ee1964e (field) :Void; # Make the generated getters return Option instead of T. Supported on # pointer types (e.g. structs, lists, and blobs). # # Capnp pointer types are nullable. Normally get_field will return the default # value if the field isn't set. With this annotation you get Some(...) when # the field is set and None when it isn't. # # Given # # struct Test { # field @0 :Text $Rust.option; # } # # you get getters like so # # assert_eq!(struct_with.get_field(), Some("foo")); # assert_eq!(struct_without.get_field(), None)); # # The setters are unchanged to match the Rust convention. # # Note: Support for this annotation on interfaces isn't implemented yet. capnpc-0.19.0/src/capnpc-rust-bootstrap.rs000064400000000000000000000007461046102023000165750ustar 00000000000000//! Schema compiler plugin specialized for the sole purpose of bootstrapping schema.capnp. //! Because the generated code lives in the capnp crate, we need to make sure that //! it uses `crate::` rather than `::capnp::` to refer to things in that crate. pub fn main() { ::capnpc::codegen::CodeGenerationCommand::new() .output_directory(::std::path::Path::new(".")) .capnp_root("crate") .run(::std::io::stdin()) .expect("failed to generate code"); } capnpc-0.19.0/src/capnpc-rust.rs000064400000000000000000000031501046102023000145520ustar 00000000000000// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors // Licensed under the MIT License: // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //! # Cap'n Proto Schema Compiler Plugin Executable //! //! [See this.](https://capnproto.org/otherlang.html#how-to-write-compiler-plugins) //! //! pub fn main() { //! Generates Rust code according to a `schema_capnp::code_generator_request` read from stdin. ::capnpc::codegen::CodeGenerationCommand::new() .output_directory(::std::path::Path::new(".")) .run(::std::io::stdin()) .expect("failed to generate code"); } capnpc-0.19.0/src/codegen.rs000064400000000000000000003721401046102023000137270ustar 00000000000000// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors // Licensed under the MIT License: // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. use std::collections; use std::collections::{HashMap, HashSet}; use std::path::{Path, PathBuf}; use capnp; use capnp::schema_capnp; use capnp::Error; use self::FormattedText::{BlankLine, Branch, Indent, Line}; use crate::codegen_types::{do_branding, Leaf, RustNodeInfo, RustTypeInfo, TypeParameterTexts}; use crate::convert_io_err; use crate::pointer_constants::generate_pointer_constant; /// An invocation of the capnpc-rust code generation plugin. pub struct CodeGenerationCommand { output_directory: PathBuf, default_parent_module: Vec, raw_code_generator_request_path: Option, capnp_root: String, crates_provide_map: HashMap, } impl Default for CodeGenerationCommand { fn default() -> Self { Self { output_directory: PathBuf::new(), default_parent_module: Vec::new(), raw_code_generator_request_path: None, capnp_root: "::capnp".into(), crates_provide_map: HashMap::new(), } } } impl CodeGenerationCommand { /// Creates a new code generation command with default options. pub fn new() -> Self { Self::default() } /// Sets the output directory. pub fn output_directory

(&mut self, path: P) -> &mut Self where P: AsRef, { self.output_directory = path.as_ref().to_path_buf(); self } /// Sets the default parent module, indicating the scope in your crate where you will /// add the generated code. /// /// This option can be overridden by the `parentModule` annotation defined in `rust.capnp`. pub fn default_parent_module(&mut self, default_parent_module: Vec) -> &mut Self { self.default_parent_module = default_parent_module; self } /// Sets the root path for referencing things in the `capnp` crate from the generated /// code. Usually this is `::capnp`. When we bootstrap schema.capnp we set this to `crate`. /// If you are renaming the `capnp` crate when importing it, then you probably want to set /// this value. pub fn capnp_root(&mut self, capnp_root: &str) -> &mut Self { self.capnp_root = capnp_root.into(); self } /// Sets the raw code generator request output path. pub fn raw_code_generator_request_path

(&mut self, path: P) -> &mut Self where P: AsRef, { self.raw_code_generator_request_path = Some(path.as_ref().to_path_buf()); self } /// Sets the crate provides map. /// /// # Arguments /// /// - `map` - A map from capnp file id to the crate name that provides the /// corresponding generated code. /// /// See [`crate::CompilerCommand::crate_provides`] for more details. pub fn crates_provide_map(&mut self, map: HashMap) -> &mut Self { self.crates_provide_map = map; self } /// Generates Rust code according to a `schema_capnp::code_generator_request` read from `inp`. pub fn run(&mut self, inp: T) -> ::capnp::Result<()> where T: std::io::Read, { use capnp::serialize; use std::io::Write; let message = serialize::read_message(inp, capnp::message::ReaderOptions::new())?; let ctx = GeneratorContext::new_from_code_generation_command(self, &message)?; for requested_file in ctx.request.get_requested_files()? { let id = requested_file.get_id(); let mut filepath = self.output_directory.to_path_buf(); let requested = ::std::path::PathBuf::from(requested_file.get_filename()?.to_str()?); filepath.push(requested); if let Some(parent) = filepath.parent() { ::std::fs::create_dir_all(parent).map_err(convert_io_err)?; } let root_name = path_to_stem_string(&filepath)?.replace('-', "_"); filepath.set_file_name(&format!("{root_name}_capnp.rs")); let lines = Branch(vec![ Line( "// @generated by the capnpc-rust plugin to the Cap'n Proto schema compiler." .to_string(), ), line("// DO NOT EDIT."), Line(format!("// source: {}", requested_file.get_filename()?.to_str()?)), BlankLine, generate_node(&ctx, id, &root_name)?, ]); let text = stringify(&lines); let previous_text = ::std::fs::read(&filepath); if previous_text.is_ok() && previous_text.unwrap() == text.as_bytes() { // File is unchanged. Do not write it so that builds with the // output as part of the source work in read-only filesystems // and so timestamp-based build systems and watchers do not get // confused. continue; } // It would be simpler to use the ? operator instead of a pattern match, but then the error message // would not include `filepath`. match ::std::fs::File::create(&filepath) { Ok(mut writer) => { writer.write_all(text.as_bytes()).map_err(convert_io_err)?; } Err(e) => { let _ = writeln!( &mut ::std::io::stderr(), "could not open file {filepath:?} for writing: {e}" ); return Err(convert_io_err(e)); } } } if let Some(raw_code_generator_request) = &self.raw_code_generator_request_path { let raw_code_generator_request_file = ::std::fs::File::create(raw_code_generator_request).map_err(convert_io_err)?; serialize::write_message_segments( raw_code_generator_request_file, &message.into_segments(), )?; } Ok(()) } } pub struct GeneratorContext<'a> { pub request: schema_capnp::code_generator_request::Reader<'a>, pub node_map: collections::hash_map::HashMap>, pub scope_map: collections::hash_map::HashMap>, /// Map from node ID to the node ID of its parent scope. This is equal to node.scope_id /// for all nodes except for autogenerated interface Param and Result structs; /// those have scope_id set to 0. See the comment on paramStructType in schema.capnp. pub node_parents: collections::hash_map::HashMap, /// Root path for referencing things in the `capnp` crate from the generated code. pub capnp_root: String, } impl<'a> GeneratorContext<'a> { pub fn new( message: &'a capnp::message::Reader, ) -> ::capnp::Result> { GeneratorContext::new_from_code_generation_command(&Default::default(), message) } fn new_from_code_generation_command( code_generation_command: &CodeGenerationCommand, message: &'a capnp::message::Reader, ) -> ::capnp::Result> { let mut default_parent_module_scope = vec!["crate".to_string()]; default_parent_module_scope .extend_from_slice(&code_generation_command.default_parent_module[..]); let mut ctx = GeneratorContext { request: message.get_root()?, node_map: collections::hash_map::HashMap::>::new(), scope_map: collections::hash_map::HashMap::>::new(), node_parents: collections::hash_map::HashMap::new(), capnp_root: code_generation_command.capnp_root.clone(), }; let crates_provide = &code_generation_command.crates_provide_map; for node in ctx.request.get_nodes()? { ctx.node_map.insert(node.get_id(), node); ctx.node_parents.insert(node.get_id(), node.get_scope_id()); } // Fix up "anonymous" method params and results scopes. for node in ctx.request.get_nodes()? { if let Ok(schema_capnp::node::Interface(interface_reader)) = node.which() { for method in interface_reader.get_methods()? { let param_struct_type = method.get_param_struct_type(); if ctx.node_parents.get(¶m_struct_type) == Some(&0) { ctx.node_parents.insert(param_struct_type, node.get_id()); } let result_struct_type = method.get_result_struct_type(); if ctx.node_parents.get(&result_struct_type) == Some(&0) { ctx.node_parents.insert(result_struct_type, node.get_id()); } } } } for requested_file in ctx.request.get_requested_files()? { let id = requested_file.get_id(); for import in requested_file.get_imports()? { let importpath = ::std::path::Path::new(import.get_name()?.to_str()?); let root_name: String = format!( "{}_capnp", path_to_stem_string(importpath)?.replace('-', "_") ); let parent_module_scope = if let Some(krate) = crates_provide.get(&import.get_id()) { vec![format!("::{krate}")] } else { default_parent_module_scope.clone() }; ctx.populate_scope_map( parent_module_scope, root_name, NameKind::Verbatim, import.get_id(), )?; } let root_name = path_to_stem_string(requested_file.get_filename()?.to_str()?)?; let root_mod = format!("{}_capnp", root_name.replace('-', "_")); ctx.populate_scope_map( default_parent_module_scope.clone(), root_mod, NameKind::Verbatim, id, )?; } Ok(ctx) } fn get_last_name(&self, id: u64) -> ::capnp::Result<&str> { match self.scope_map.get(&id) { None => Err(Error::failed(format!("node not found: {id}"))), Some(v) => match v.last() { None => Err(Error::failed(format!("node has no scope: {id}"))), Some(n) => Ok(n), }, } } fn populate_scope_map( &mut self, mut ancestor_scope_names: Vec, mut current_node_name: String, current_name_kind: NameKind, node_id: u64, ) -> ::capnp::Result<()> { // unused nodes in imported files might be omitted from the node map let Some(&node_reader) = self.node_map.get(&node_id) else { return Ok(()); }; for annotation in node_reader.get_annotations()? { if annotation.get_id() == NAME_ANNOTATION_ID { current_node_name = name_annotation_value(annotation)?.to_string(); } else if annotation.get_id() == PARENT_MODULE_ANNOTATION_ID { let head = ancestor_scope_names[0].clone(); ancestor_scope_names.clear(); ancestor_scope_names.push(head); ancestor_scope_names.append(&mut get_parent_module(annotation)?); } } let mut scope_names = ancestor_scope_names; scope_names.push(capnp_name_to_rust_name( ¤t_node_name, current_name_kind, )); self.scope_map.insert(node_id, scope_names.clone()); let nested_nodes = node_reader.get_nested_nodes()?; for nested_node in nested_nodes { let nested_node_id = nested_node.get_id(); match self.node_map.get(&nested_node_id) { None => {} Some(node_reader) => match node_reader.which() { Ok(schema_capnp::node::Enum(_enum_reader)) => { self.populate_scope_map( scope_names.clone(), nested_node.get_name()?.to_string()?, NameKind::Verbatim, nested_node_id, )?; } _ => { self.populate_scope_map( scope_names.clone(), nested_node.get_name()?.to_string()?, NameKind::Module, nested_node_id, )?; } }, } } if let Ok(schema_capnp::node::Struct(struct_reader)) = node_reader.which() { let fields = struct_reader.get_fields()?; for field in fields { if let Ok(schema_capnp::field::Group(group)) = field.which() { self.populate_scope_map( scope_names.clone(), get_field_name(field)?.to_string(), NameKind::Module, group.get_type_id(), )?; } } } Ok(()) } pub fn get_qualified_module(&self, type_id: u64) -> String { self.scope_map[&type_id].join("::") } } /// Like `format!(...)`, but adds a `capnp=ctx.capnp_root` argument. macro_rules! fmt( ($ctx:ident, $($arg:tt)*) => ( format!($($arg)*, capnp=$ctx.capnp_root) ) ); pub(crate) use fmt; fn path_to_stem_string>(path: P) -> ::capnp::Result { if let Some(stem) = path.as_ref().file_stem() { stem.to_owned() .into_string() .map_err(|os_string| Error::failed(format!("bad filename: {os_string:?}"))) } else { Err(Error::failed(format!( "file has no stem: {:?}", path.as_ref() ))) } } fn snake_to_upper_case(s: &str) -> String { let mut result_chars: Vec = Vec::new(); for c in s.chars() { if c == '_' { result_chars.push('_'); } else { result_chars.push(c.to_ascii_uppercase()); } } result_chars.into_iter().collect() } fn camel_to_snake_case(s: &str) -> String { let mut result_chars: Vec = Vec::new(); let mut first_char = true; for c in s.chars() { if c.is_uppercase() && !first_char { result_chars.push('_'); } result_chars.push(c.to_ascii_lowercase()); first_char = false; } result_chars.into_iter().collect() } fn capitalize_first_letter(s: &str) -> String { let mut result_chars: Vec = Vec::new(); for c in s.chars() { result_chars.push(c) } result_chars[0] = result_chars[0].to_ascii_uppercase(); result_chars.into_iter().collect() } /// Formats a u64 into a string representation of the hex value, with /// separating underscores. Used instead of simple hex formatting to prevent /// clippy warnings in autogenerated code. This is loosely based off of /// similar functionality in the `separator` crate. fn format_u64(value: u64) -> String { let hex = format!("{value:#x}"); let mut separated = hex[0..2].to_string(); let mut place = hex.len() - 2; let mut later_loop = false; for ch in hex[2..].chars() { if later_loop && place % 4 == 0 { separated.push('_'); } separated.push(ch); later_loop = true; place -= 1; } separated } #[test] fn test_camel_to_snake_case() { assert_eq!(camel_to_snake_case("fooBar"), "foo_bar".to_string()); assert_eq!(camel_to_snake_case("FooBar"), "foo_bar".to_string()); assert_eq!(camel_to_snake_case("fooBarBaz"), "foo_bar_baz".to_string()); assert_eq!(camel_to_snake_case("FooBarBaz"), "foo_bar_baz".to_string()); assert_eq!(camel_to_snake_case("helloWorld"), "hello_world".to_string()); assert_eq!(camel_to_snake_case("HelloWorld"), "hello_world".to_string()); assert_eq!(camel_to_snake_case("uint32Id"), "uint32_id".to_string()); assert_eq!(camel_to_snake_case("fooBar_"), "foo_bar_".to_string()); } #[derive(PartialEq, Clone)] pub enum FormattedText { Indent(Box), Branch(Vec), Line(String), BlankLine, } impl From> for FormattedText { fn from(value: Vec) -> Self { Branch(value) } } pub fn indent(inner: impl Into) -> FormattedText { Indent(Box::new(inner.into())) } pub fn line(inner: impl ToString) -> FormattedText { Line(inner.to_string()) } fn to_lines(ft: &FormattedText, indent: usize) -> Vec { match ft { Indent(ft) => to_lines(ft, indent + 1), Branch(fts) => fts.iter().flat_map(|ft| to_lines(ft, indent)).collect(), Line(s) => { let mut s1: String = " ".repeat(indent * 2); s1.push_str(s); vec![s1.to_string()] } BlankLine => vec!["".to_string()], } } fn stringify(ft: &FormattedText) -> String { let mut result = to_lines(ft, 0).join("\n"); result.push('\n'); result } const RUST_KEYWORDS: &[&str] = &[ "abstract", "alignof", "as", "be", "become", "box", "break", "const", "continue", "crate", "do", "else", "enum", "extern", "false", "final", "fn", "for", "if", "impl", "in", "let", "loop", "macro", "match", "mod", "move", "mut", "offsetof", "once", "override", "priv", "proc", "pub", "pure", "ref", "return", "self", "sizeof", "static", "struct", "super", "trait", "true", "type", "typeof", "unsafe", "unsized", "use", "virtual", "where", "while", "yield", ]; fn module_name(camel_case: &str) -> String { let mut name = camel_to_snake_case(camel_case); if RUST_KEYWORDS.contains(&&*name) { name.push('_'); } name } // Annotation IDs, as defined in rust.capnp. const NAME_ANNOTATION_ID: u64 = 0xc2fe4c6d100166d0; const PARENT_MODULE_ANNOTATION_ID: u64 = 0xabee386cd1450364; const OPTION_ANNOTATION_ID: u64 = 0xabfef22c4ee1964e; fn name_annotation_value(annotation: schema_capnp::annotation::Reader) -> capnp::Result<&str> { if let schema_capnp::value::Text(t) = annotation.get_value()?.which()? { let name = t?.to_str()?; for c in name.chars() { if !(c == '_' || c.is_alphanumeric()) { return Err(capnp::Error::failed( "rust.name annotation value must only contain alphanumeric characters and '_'" .to_string(), )); } } Ok(name) } else { Err(capnp::Error::failed( "expected rust.name annotation value to be of type Text".to_string(), )) } } fn get_field_name(field: schema_capnp::field::Reader) -> capnp::Result<&str> { for annotation in field.get_annotations()? { if annotation.get_id() == NAME_ANNOTATION_ID { return name_annotation_value(annotation); } } Ok(field.get_name()?.to_str()?) } fn get_enumerant_name(enumerant: schema_capnp::enumerant::Reader) -> capnp::Result<&str> { for annotation in enumerant.get_annotations()? { if annotation.get_id() == NAME_ANNOTATION_ID { return name_annotation_value(annotation); } } Ok(enumerant.get_name()?.to_str()?) } fn get_parent_module(annotation: schema_capnp::annotation::Reader) -> capnp::Result> { if let schema_capnp::value::Text(t) = annotation.get_value()?.which()? { let module = t?.to_str()?; Ok(module.split("::").map(|x| x.to_string()).collect()) } else { Err(capnp::Error::failed( "expected rust.parentModule annotation value to be of type Text".to_string(), )) } } #[derive(Clone, Copy)] enum NameKind { // convert camel case to snake case, and avoid Rust keywords Module, // don't modify Verbatim, } fn capnp_name_to_rust_name(capnp_name: &str, name_kind: NameKind) -> String { match name_kind { NameKind::Module => module_name(capnp_name), NameKind::Verbatim => capnp_name.to_string(), } } fn is_option_field(field: schema_capnp::field::Reader) -> capnp::Result { use capnp::schema_capnp::*; let enabled = field .get_annotations()? .iter() .any(|a| a.get_id() == OPTION_ANNOTATION_ID); if enabled { let supported = match field.which()? { field::Which::Group(_) => false, field::Which::Slot(field) => { let ty = field.get_type()?; ty.is_pointer()? && !matches!(ty.which()?, type_::Interface(_)) } }; if !supported { return Err(capnp::Error::failed( "$Rust.option annotation only supported on pointer fields (support for optional interfaces isn't implemented yet)".to_string(), )); } } Ok(enabled) } fn prim_default(value: &schema_capnp::value::Reader) -> ::capnp::Result> { use capnp::schema_capnp::value; match value.which()? { value::Bool(false) | value::Int8(0) | value::Int16(0) | value::Int32(0) | value::Int64(0) | value::Uint8(0) | value::Uint16(0) | value::Uint32(0) | value::Uint64(0) => Ok(None), value::Bool(true) => Ok(Some("true".to_string())), value::Int8(i) => Ok(Some(i.to_string())), value::Int16(i) => Ok(Some(i.to_string())), value::Int32(i) => Ok(Some(i.to_string())), value::Int64(i) => Ok(Some(i.to_string())), value::Uint8(i) => Ok(Some(i.to_string())), value::Uint16(i) => Ok(Some(i.to_string())), value::Uint32(i) => Ok(Some(i.to_string())), value::Uint64(i) => Ok(Some(i.to_string())), value::Float32(f) => match f.classify() { ::std::num::FpCategory::Zero => Ok(None), _ => Ok(Some(format!("{}u32", f.to_bits()))), }, value::Float64(f) => match f.classify() { ::std::num::FpCategory::Zero => Ok(None), _ => Ok(Some(format!("{}u64", f.to_bits()))), }, _ => Err(Error::failed( "Non-primitive value found where primitive was expected.".to_string(), )), } } // Gets the full list ordered of generic parameters for a node. Outer scopes come first. fn get_params(ctx: &GeneratorContext, mut node_id: u64) -> ::capnp::Result> { let mut result = Vec::new(); while node_id != 0 { let node = ctx.node_map[&node_id]; let parameters = node.get_parameters()?; for parameter in parameters.into_iter().rev() { result.push(parameter.get_name()?.to_str()?.into()); } node_id = node.get_scope_id(); } result.reverse(); Ok(result) } // // Returns (type, getter body, default_decl) // pub fn getter_text( ctx: &GeneratorContext, field: &schema_capnp::field::Reader, is_reader: bool, is_fn: bool, ) -> ::capnp::Result<(String, FormattedText, Option)> { use capnp::schema_capnp::*; match field.which()? { field::Group(group) => { let params = get_params(ctx, group.get_type_id())?; let params_string = if params.is_empty() { "".to_string() } else { format!(",{}", params.join(",")) }; let the_mod = ctx.get_qualified_module(group.get_type_id()); let mut result_type = if is_reader { format!("{the_mod}::Reader<'a{params_string}>") } else { format!("{the_mod}::Builder<'a{params_string}>") }; if is_fn { result_type = format!("-> {result_type}"); } let getter_code = if is_reader { line("self.reader.into()") } else { line("self.builder.into()") }; Ok((result_type, getter_code, None)) } field::Slot(reg_field) => { let mut default_decl = None; let offset = reg_field.get_offset() as usize; let module_string = if is_reader { "Reader" } else { "Builder" }; let module = if is_reader { Leaf::Reader("'a") } else { Leaf::Builder("'a") }; let member = camel_to_snake_case(module_string); fn primitive_case( typ: &str, member: &str, offset: usize, default: T, zero: T, ) -> String { if default == zero { format!("self.{member}.get_data_field::<{typ}>({offset})") } else { format!("self.{member}.get_data_field_mask::<{typ}>({offset}, {default})") } } let raw_type = reg_field.get_type()?; let inner_type = raw_type.type_string(ctx, module)?; let default_value = reg_field.get_default_value()?; let default = default_value.which()?; let default_name = format!( "DEFAULT_{}", snake_to_upper_case(&camel_to_snake_case(get_field_name(*field)?)) ); let should_get_option = is_option_field(*field)?; let typ = if should_get_option { format!("Option<{}>", inner_type) } else { inner_type }; let (is_fallible, mut result_type) = match raw_type.which()? { type_::Enum(_) => ( true, fmt!(ctx, "::core::result::Result<{typ},{capnp}::NotInSchema>"), ), type_::AnyPointer(_) if !raw_type.is_parameter()? => (false, typ.clone()), type_::Interface(_) => ( true, fmt!( ctx, "{capnp}::Result<{}>", raw_type.type_string(ctx, Leaf::Client)? ), ), _ if raw_type.is_prim()? => (false, typ.clone()), _ => (true, fmt!(ctx, "{capnp}::Result<{typ}>")), }; if is_fn { result_type = if result_type == "()" { "".to_string() } else { format!("-> {result_type}") } } let getter_fragment = match (raw_type.which()?, default) { (type_::Void(()), value::Void(())) => { if is_fn { "".to_string() } else { "()".to_string() } } (type_::Bool(()), value::Bool(b)) => { if b { format!("self.{member}.get_bool_field_mask({offset}, true)") } else { format!("self.{member}.get_bool_field({offset})") } } (type_::Int8(()), value::Int8(i)) => primitive_case(&typ, &member, offset, i, 0), (type_::Int16(()), value::Int16(i)) => primitive_case(&typ, &member, offset, i, 0), (type_::Int32(()), value::Int32(i)) => primitive_case(&typ, &member, offset, i, 0), (type_::Int64(()), value::Int64(i)) => primitive_case(&typ, &member, offset, i, 0), (type_::Uint8(()), value::Uint8(i)) => primitive_case(&typ, &member, offset, i, 0), (type_::Uint16(()), value::Uint16(i)) => { primitive_case(&typ, &member, offset, i, 0) } (type_::Uint32(()), value::Uint32(i)) => { primitive_case(&typ, &member, offset, i, 0) } (type_::Uint64(()), value::Uint64(i)) => { primitive_case(&typ, &member, offset, i, 0) } (type_::Float32(()), value::Float32(f)) => { primitive_case(&typ, &member, offset, f.to_bits(), 0) } (type_::Float64(()), value::Float64(f)) => { primitive_case(&typ, &member, offset, f.to_bits(), 0) } (type_::Enum(_), value::Enum(d)) => { if d == 0 { format!("::core::convert::TryInto::try_into(self.{member}.get_data_field::({offset}))") } else { format!( "::core::convert::TryInto::try_into(self.{member}.get_data_field_mask::({offset}, {d}))") } } (type_::Text(()), value::Text(_)) | (type_::Data(()), value::Data(_)) | (type_::List(_), value::List(_)) | (type_::Struct(_), value::Struct(_)) => { let default = if reg_field.get_had_explicit_default() { default_decl = Some(crate::pointer_constants::word_array_declaration( ctx, &default_name, ::capnp::raw::get_struct_pointer_section(default_value).get(0), crate::pointer_constants::WordArrayDeclarationOptions { public: true }, )?); format!("::core::option::Option::Some(&_private::{default_name}[..])") } else { "::core::option::Option::None".to_string() }; if is_reader { fmt!(ctx, "{capnp}::traits::FromPointerReader::get_from_pointer(&self.{member}.get_pointer_field({offset}), {default})") } else { fmt!(ctx,"{capnp}::traits::FromPointerBuilder::get_from_pointer(self.{member}.get_pointer_field({offset}), {default})") } } (type_::Interface(_), value::Interface(_)) => { 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)}}") } (type_::AnyPointer(_), value::AnyPointer(_)) => { if !raw_type.is_parameter()? { fmt!(ctx,"{capnp}::any_pointer::{module_string}::new(self.{member}.get_pointer_field({offset}))") } else if is_reader { fmt!(ctx,"{capnp}::traits::FromPointerReader::get_from_pointer(&self.{member}.get_pointer_field({offset}), ::core::option::Option::None)") } else { fmt!(ctx,"{capnp}::traits::FromPointerBuilder::get_from_pointer(self.{member}.get_pointer_field({offset}), ::core::option::Option::None)") } } _ => return Err(Error::failed("default value was of wrong type".to_string())), }; let getter_code = if should_get_option { Branch(vec![ Line(format!( "if self.{member}.is_pointer_field_null({offset}) {{" )), indent(Line( if is_fallible { "core::result::Result::Ok(core::option::Option::None)" } else { "::core::option::Option::None" } .to_string(), )), Line("} else {".to_string()), indent(Line(if is_fallible { format!("{getter_fragment}.map(::core::option::Option::Some)") } else { format!("::core::option::Option::Some({getter_fragment})") })), Line("}".to_string()), ]) } else { Line(getter_fragment) }; Ok((result_type, getter_code, default_decl)) } } } fn zero_fields_of_group( ctx: &GeneratorContext, node_id: u64, clear: &mut bool, ) -> ::capnp::Result { use capnp::schema_capnp::{field, node, type_}; match ctx.node_map[&node_id].which()? { node::Struct(st) => { let mut result = Vec::new(); if st.get_discriminant_count() != 0 { result.push(Line(format!( "self.builder.set_data_field::({}, 0);", st.get_discriminant_offset() ))); } let fields = st.get_fields()?; for field in fields { match field.which()? { field::Group(group) => { result.push(zero_fields_of_group(ctx, group.get_type_id(), clear)?); } field::Slot(slot) => { let typ = slot.get_type()?.which()?; match typ { type_::Void(()) => {} type_::Bool(()) => { let line = Line(format!( "self.builder.set_bool_field({}, false);", slot.get_offset() )); // PERF could dedup more efficiently if !result.contains(&line) { result.push(line) } } type_::Int8(()) | type_::Int16(()) | type_::Int32(()) | type_::Int64(()) | type_::Uint8(()) | type_::Uint16(()) | type_::Uint32(()) | type_::Uint64(()) | type_::Float32(()) | type_::Float64(()) => { let line = Line(format!( "self.builder.set_data_field::<{0}>({1}, 0{0});", slot.get_type()?.type_string(ctx, Leaf::Builder("'a"))?, slot.get_offset() )); // PERF could dedup more efficiently if !result.contains(&line) { result.push(line) } } type_::Enum(_) => { let line = Line(format!( "self.builder.set_data_field::({}, 0u16);", slot.get_offset() )); // PERF could dedup more efficiently if !result.contains(&line) { result.push(line) } } type_::Struct(_) | type_::List(_) | type_::Text(()) | type_::Data(()) | type_::AnyPointer(_) | type_::Interface(_) => { // Is this the right thing to do for interfaces? let line = Line(format!( "self.builder.reborrow().get_pointer_field({}).clear();", slot.get_offset() )); *clear = true; // PERF could dedup more efficiently if !result.contains(&line) { result.push(line) } } } } } } Ok(Branch(result)) } _ => Err(Error::failed( "zero_fields_of_groupd() expected a struct".to_string(), )), } } fn generate_setter( ctx: &GeneratorContext, discriminant_offset: u32, styled_name: &str, field: &schema_capnp::field::Reader, ) -> ::capnp::Result { use capnp::schema_capnp::*; let mut setter_interior = Vec::new(); let mut setter_param = "value".to_string(); let mut initter_interior = Vec::new(); let mut initter_mut = false; let mut initn_interior = Vec::new(); let mut initter_params = Vec::new(); let discriminant_value = field.get_discriminant_value(); if discriminant_value != field::NO_DISCRIMINANT { setter_interior.push(Line(format!( "self.builder.set_data_field::({}, {});", discriminant_offset as usize, discriminant_value as usize ))); let init_discrim = Line(format!( "self.builder.set_data_field::({}, {});", discriminant_offset as usize, discriminant_value as usize )); initter_interior.push(init_discrim.clone()); initn_interior.push(init_discrim); } let mut return_result = false; let mut result = Vec::new(); let (maybe_reader_type, maybe_builder_type): (Option, Option) = match field .which()? { field::Group(group) => { let params = get_params(ctx, group.get_type_id())?; let params_string = if params.is_empty() { "".to_string() } else { format!(",{}", params.join(",")) }; let the_mod = ctx.get_qualified_module(group.get_type_id()); initter_interior.push(zero_fields_of_group( ctx, group.get_type_id(), &mut initter_mut, )?); initter_interior.push(line("self.builder.into()")); (None, Some(format!("{the_mod}::Builder<'a{params_string}>"))) } field::Slot(reg_field) => { let offset = reg_field.get_offset() as usize; let typ = reg_field.get_type()?; match typ.which().expect("unrecognized type") { type_::Void(()) => { setter_param = "_value".to_string(); (Some("()".to_string()), None) } type_::Bool(()) => { match prim_default(®_field.get_default_value()?)? { None => { setter_interior.push(Line(format!( "self.builder.set_bool_field({offset}, value);" ))); } Some(s) => { setter_interior.push(Line(format!( "self.builder.set_bool_field_mask({offset}, value, {s});" ))); } } (Some("bool".to_string()), None) } _ if typ.is_prim()? => { let tstr = typ.type_string(ctx, Leaf::Reader("'a"))?; match prim_default(®_field.get_default_value()?)? { None => { setter_interior.push(Line(format!( "self.builder.set_data_field::<{tstr}>({offset}, value);" ))); } Some(s) => { setter_interior.push(Line(format!( "self.builder.set_data_field_mask::<{tstr}>({offset}, value, {s});" ))); } }; (Some(tstr), None) } type_::Text(()) => { // The text::Reader impl of SetterInput never fails, so we can unwrap(). setter_interior.push(Line(fmt!(ctx, "{capnp}::traits::SetterInput::set_pointer_builder(self.builder.reborrow().get_pointer_field({offset}), value, false).unwrap()" ))); initter_interior.push(Line(format!( "self.builder.get_pointer_field({offset}).init_text(size)" ))); initter_params.push("size: u32"); ( Some(fmt!( ctx, "impl {capnp}::traits::SetterInput<{capnp}::text::Owned>" )), Some(fmt!(ctx, "{capnp}::text::Builder<'a>")), ) } type_::Data(()) => { setter_interior.push(Line(format!( "self.builder.reborrow().get_pointer_field({offset}).set_data(value);" ))); initter_interior.push(Line(format!( "self.builder.get_pointer_field({offset}).init_data(size)" ))); initter_params.push("size: u32"); ( Some(fmt!(ctx, "{capnp}::data::Reader<'_>")), Some(fmt!(ctx, "{capnp}::data::Builder<'a>")), ) } type_::List(ls) => { let et = ls.get_element_type()?; return_result = true; setter_interior.push( Line(fmt!(ctx,"{capnp}::traits::SetterInput::set_pointer_builder(self.builder.reborrow().get_pointer_field({offset}), value, false)"))); initter_params.push("size: u32"); initter_interior.push( Line(fmt!(ctx,"{capnp}::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field({offset}), size)"))); let mr = match et.which()? { type_::Void(()) | type_::Bool(()) | type_::Int8(()) | type_::Int16(()) | type_::Int32(()) | type_::Int64(()) | type_::Uint8(()) | type_::Uint16(()) | type_::Uint32(()) | type_::Uint64(()) | type_::Float32(()) | type_::Float64(()) | type_::Enum(_) | type_::Text(()) => { // There are multiple SetterInput impls. fmt!( ctx, "impl {capnp}::traits::SetterInput<{}>", reg_field.get_type()?.type_string(ctx, Leaf::Owned)? ) } _ => reg_field.get_type()?.type_string(ctx, Leaf::Reader("'_"))?, }; ( Some(mr), Some( reg_field .get_type()? .type_string(ctx, Leaf::Builder("'a"))?, ), ) } type_::Enum(e) => { let id = e.get_type_id(); let the_mod = ctx.get_qualified_module(id); if !reg_field.get_had_explicit_default() { setter_interior.push(Line(format!( "self.builder.set_data_field::({offset}, value as u16);" ))); } else { match reg_field.get_default_value()?.which()? { schema_capnp::value::Enum(d) => { setter_interior.push(Line(format!( "self.builder.set_data_field_mask::({offset}, value as u16, {d});" ))); } _ => return Err(Error::failed("enum default not an Enum".to_string())), } }; (Some(the_mod), None) } type_::Struct(_) => { return_result = true; initter_interior.push( Line(fmt!(ctx,"{capnp}::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field({offset}), 0)"))); setter_interior.push( Line(fmt!(ctx,"{capnp}::traits::SetterInput::set_pointer_builder(self.builder.reborrow().get_pointer_field({offset}), value, false)"))); ( Some(typ.type_string(ctx, Leaf::Reader("'_"))?), Some(typ.type_string(ctx, Leaf::Builder("'a"))?), ) } type_::Interface(_) => { setter_interior.push(Line(format!( "self.builder.reborrow().get_pointer_field({offset}).set_capability(value.client.hook);" ))); (Some(typ.type_string(ctx, Leaf::Client)?), None) } type_::AnyPointer(_) => { if typ.is_parameter()? { initter_interior.push(Line(fmt!(ctx,"{capnp}::any_pointer::Builder::new(self.builder.get_pointer_field({offset})).init_as()"))); setter_interior.push(Line(fmt!(ctx,"{capnp}::traits::SetterInput::set_pointer_builder(self.builder.reborrow().get_pointer_field({offset}), value, false)"))); return_result = true; let builder_type = typ.type_string(ctx, Leaf::Builder("'a"))?; result.push(line("#[inline]")); result.push(Line(format!( "pub fn initn_{styled_name}(self, length: u32) -> {builder_type} {{" ))); result.push(indent(initn_interior)); result.push(indent( Line(fmt!(ctx,"{capnp}::any_pointer::Builder::new(self.builder.get_pointer_field({offset})).initn_as(length)"))) ); result.push(line("}")); ( Some(fmt!( ctx, "impl {capnp}::traits::SetterInput<{}>", typ.type_string(ctx, Leaf::Owned)? )), Some(builder_type), ) } else { initter_interior.push(Line(fmt!(ctx,"let mut result = {capnp}::any_pointer::Builder::new(self.builder.get_pointer_field({offset}));"))); initter_interior.push(line("result.clear();")); initter_interior.push(line("result")); (None, Some(fmt!(ctx, "{capnp}::any_pointer::Builder<'a>"))) } } _ => return Err(Error::failed("unrecognized type".to_string())), } } }; if let Some(reader_type) = maybe_reader_type { let return_type = if return_result { fmt!(ctx, "-> {capnp}::Result<()>") } else { "".into() }; result.push(line("#[inline]")); result.push(Line(format!( "pub fn set_{styled_name}(&mut self, {setter_param}: {reader_type}) {return_type} {{" ))); result.push(indent(setter_interior)); result.push(line("}")); } if let Some(builder_type) = maybe_builder_type { result.push(line("#[inline]")); let args = initter_params.join(", "); let mutable = if initter_mut { "mut " } else { "" }; result.push(Line(format!( "pub fn init_{styled_name}({mutable}self, {args}) -> {builder_type} {{" ))); result.push(indent(initter_interior)); result.push(line("}")); } Ok(Branch(result)) } fn used_params_of_group( ctx: &GeneratorContext, group_id: u64, used_params: &mut HashSet, ) -> capnp::Result<()> { let node = ctx.node_map[&group_id]; match node.which()? { schema_capnp::node::Struct(st) => { for field in st.get_fields()? { match field.which()? { schema_capnp::field::Group(group) => { used_params_of_group(ctx, group.get_type_id(), used_params)?; } schema_capnp::field::Slot(slot) => { used_params_of_type(ctx, slot.get_type()?, used_params)?; } } } Ok(()) } _ => Err(Error::failed("not a group".to_string())), } } fn used_params_of_type( ctx: &GeneratorContext, ty: schema_capnp::type_::Reader, used_params: &mut HashSet, ) -> capnp::Result<()> { use capnp::schema_capnp::type_; match ty.which()? { type_::List(ls) => { let et = ls.get_element_type()?; used_params_of_type(ctx, et, used_params)?; } type_::Enum(e) => { let node_id = e.get_type_id(); let brand = e.get_brand()?; used_params_of_brand(ctx, node_id, brand, used_params)?; } type_::Struct(s) => { let node_id = s.get_type_id(); let brand = s.get_brand()?; used_params_of_brand(ctx, node_id, brand, used_params)?; } type_::Interface(i) => { let node_id = i.get_type_id(); let brand = i.get_brand()?; used_params_of_brand(ctx, node_id, brand, used_params)?; } type_::AnyPointer(ap) => { if let type_::any_pointer::Parameter(def) = ap.which()? { let the_struct = &ctx.node_map[&def.get_scope_id()]; let parameters = the_struct.get_parameters()?; let parameter = parameters.get(u32::from(def.get_parameter_index())); let parameter_name = parameter.get_name()?.to_str()?; used_params.insert(parameter_name.to_string()); } } _ => (), } Ok(()) } fn used_params_of_brand( ctx: &GeneratorContext, node_id: u64, brand: schema_capnp::brand::Reader, used_params: &mut HashSet, ) -> capnp::Result<()> { use schema_capnp::brand; let scopes = brand.get_scopes()?; let mut brand_scopes = HashMap::new(); for scope in scopes { brand_scopes.insert(scope.get_scope_id(), scope); } let brand_scopes = brand_scopes; // freeze let mut current_node_id = node_id; loop { let Some(current_node) = ctx.node_map.get(¤t_node_id) else { break; }; let params = current_node.get_parameters()?; match brand_scopes.get(¤t_node_id) { None => (), Some(scope) => match scope.which()? { brand::scope::Inherit(()) => { for param in params { used_params.insert(param.get_name()?.to_string()?); } } brand::scope::Bind(bindings_list_opt) => { let bindings_list = bindings_list_opt?; assert_eq!(bindings_list.len(), params.len()); for binding in bindings_list { match binding.which()? { brand::binding::Unbound(()) => (), brand::binding::Type(t) => { used_params_of_type(ctx, t?, used_params)?; } } } } }, } current_node_id = current_node.get_scope_id(); } Ok(()) } // return (the 'Which' enum, the 'which()' accessor, typedef, default_decls) fn generate_union( ctx: &GeneratorContext, discriminant_offset: u32, fields: &[schema_capnp::field::Reader], is_reader: bool, params: &TypeParameterTexts, ) -> ::capnp::Result<( FormattedText, FormattedText, FormattedText, Vec, )> { use capnp::schema_capnp::*; fn new_ty_param(ty_params: &mut Vec) -> String { let result = format!("A{}", ty_params.len()); ty_params.push(result.clone()); result } let mut getter_interior = Vec::new(); let mut interior = Vec::new(); let mut enum_interior = Vec::new(); let mut default_decls = Vec::new(); let mut ty_params = Vec::new(); let mut ty_args = Vec::new(); let mut used_params: HashSet = HashSet::new(); let doffset = discriminant_offset as usize; for field in fields { let dvalue = field.get_discriminant_value() as usize; let field_name = get_field_name(*field)?; let enumerant_name = capitalize_first_letter(field_name); let (ty, get, maybe_default_decl) = getter_text(ctx, field, is_reader, false)?; if let Some(default_decl) = maybe_default_decl { default_decls.push(default_decl) } getter_interior.push(Branch(vec![ Line(format!("{dvalue} => {{")), indent(Line(format!( "::core::result::Result::Ok({}(", enumerant_name.clone() ))), indent(indent(get)), indent(line("))")), line("}"), ])); let ty1 = match field.which() { Ok(field::Group(group)) => { used_params_of_group(ctx, group.get_type_id(), &mut used_params)?; ty_args.push(ty); new_ty_param(&mut ty_params) } Ok(field::Slot(reg_field)) => { let fty = reg_field.get_type()?; used_params_of_type(ctx, fty, &mut used_params)?; match fty.which() { Ok( type_::Text(()) | type_::Data(()) | type_::List(_) | type_::Struct(_) | type_::AnyPointer(_), ) => { ty_args.push(ty); new_ty_param(&mut ty_params) } Ok(type_::Interface(_)) => ty, _ => ty, } } _ => ty, }; enum_interior.push(Line(format!("{enumerant_name}({ty1}),"))); } let enum_name = format!( "Which{}", if !ty_params.is_empty() { format!("<{}>", ty_params.join(",")) } else { "".to_string() } ); getter_interior.push(Line(fmt!( ctx, "x => ::core::result::Result::Err({capnp}::NotInSchema(x))" ))); interior.push(Branch(vec![ Line(format!("pub enum {enum_name} {{")), indent(enum_interior), line("}"), ])); let result = Branch(interior); let field_name = if is_reader { "reader" } else { "builder" }; let concrete_type = format!( "Which{}{}", if is_reader { "Reader" } else { "Builder" }, if !ty_params.is_empty() { format!( "<'a,{}>", params .expanded_list .iter() .filter(|s: &&String| used_params.contains(*s)) .cloned() .collect::>() .join(",") ) } else { "".to_string() } ); let typedef = Line(format!( "pub type {concrete_type} = Which{};", if !ty_args.is_empty() { format!("<{}>", ty_args.join(",")) } else { "".to_string() } )); let getter_result = Branch(vec![ line("#[inline]"), Line(fmt!(ctx, "pub fn which(self) -> ::core::result::Result<{concrete_type}, {capnp}::NotInSchema> {{" )), indent(vec![ Line(format!( "match self.{field_name}.get_data_field::({doffset}) {{" )), indent(getter_interior), line("}"), ]), line("}"), ]); // TODO set_which() for builders? Ok((result, getter_result, typedef, default_decls)) } fn generate_haser( discriminant_offset: u32, styled_name: &str, field: &schema_capnp::field::Reader, is_reader: bool, ) -> ::capnp::Result { use capnp::schema_capnp::*; let mut result = Vec::new(); let mut interior = Vec::new(); let member = if is_reader { "reader" } else { "builder" }; let discriminant_value = field.get_discriminant_value(); if discriminant_value != field::NO_DISCRIMINANT { interior.push(Line(format!( "if self.{}.get_data_field::({}) != {} {{ return false; }}", member, discriminant_offset as usize, discriminant_value as usize ))); } match field.which() { Err(_) | Ok(field::Group(_)) => {} Ok(field::Slot(reg_field)) => match reg_field.get_type()?.which()? { type_::Text(()) | type_::Data(()) | type_::List(_) | type_::Struct(_) | type_::Interface(_) | type_::AnyPointer(_) => { if is_reader { interior.push(Line(format!( "!self.{member}.get_pointer_field({}).is_null()", reg_field.get_offset() ))); } else { interior.push(Line(format!( "!self.{member}.is_pointer_field_null({})", reg_field.get_offset() ))); } result.push(line("#[inline]")); result.push(Line(format!("pub fn has_{styled_name}(&self) -> bool {{"))); result.push(indent(interior)); result.push(line("}")); } _ => {} }, } Ok(Branch(result)) } fn generate_pipeline_getter( ctx: &GeneratorContext, field: schema_capnp::field::Reader, ) -> ::capnp::Result { use capnp::schema_capnp::{field, type_}; let name = get_field_name(field)?; match field.which()? { field::Group(group) => { let params = get_params(ctx, group.get_type_id())?; let params_string = if params.is_empty() { "".to_string() } else { format!("<{}>", params.join(",")) }; let the_mod = ctx.get_qualified_module(group.get_type_id()); Ok(Branch(vec![ Line(format!( "pub fn get_{}(&self) -> {}::Pipeline{} {{", camel_to_snake_case(name), the_mod, params_string )), indent(Line(fmt!( ctx, "{capnp}::capability::FromTypelessPipeline::new(self._typeless.noop())" ))), line("}"), ])) } field::Slot(reg_field) => { let typ = reg_field.get_type()?; match typ.which()? { type_::Struct(_) | type_::AnyPointer(_) => { Ok(Branch(vec![ Line(format!("pub fn get_{}(&self) -> {} {{", camel_to_snake_case(name), typ.type_string(ctx, Leaf::Pipeline)?)), indent(Line(fmt!(ctx,"{capnp}::capability::FromTypelessPipeline::new(self._typeless.get_pointer_field({}))", reg_field.get_offset()))), line("}") ])) } type_::Interface(_) => { Ok(Branch(vec![ Line(format!("pub fn get_{}(&self) -> {} {{", camel_to_snake_case(name), typ.type_string(ctx, Leaf::Client)?)), indent(Line(fmt!(ctx,"{capnp}::capability::FromClientHook::new(self._typeless.get_pointer_field({}).as_cap())", reg_field.get_offset()))), line("}") ])) } _ => { Ok(Branch(Vec::new())) } } } } } fn generate_get_field_types( ctx: &GeneratorContext, node_reader: schema_capnp::node::Reader, ) -> ::capnp::Result { use capnp::schema_capnp::field; let st = match node_reader.which()? { schema_capnp::node::Struct(st) => st, _ => return Err(Error::failed("not a struct".into())), }; let mut branches = vec![]; for (index, field) in st.get_fields()?.iter().enumerate() { match field.which()? { field::Slot(slot) => { let raw_type = slot.get_type()?; let typ = raw_type.type_string(ctx, Leaf::Owned)?; branches.push(Line(fmt!( ctx, "{} => <{} as {capnp}::introspect::Introspect>::introspect(),", index, typ ))); } field::Group(group) => { let params = get_params(ctx, group.get_type_id())?; let params_string = if params.is_empty() { "".to_string() } else { format!("<{}>", params.join(",")) }; let the_mod = ctx.get_qualified_module(group.get_type_id()); let typ = format!("{the_mod}::Owned{params_string}"); branches.push(Line(fmt!( ctx, "{} => <{} as {capnp}::introspect::Introspect>::introspect(),", index, typ ))); } } } let body = if branches.is_empty() { Line("panic!(\"invalid field index {}\", index)".into()) } else { branches.push(Line( "_ => panic!(\"invalid field index {}\", index),".into(), )); Branch(vec![ Line("match index {".into()), indent(branches), Line("}".into()), ]) }; if !node_reader.get_is_generic() { Ok(Branch(vec![ Line(fmt!( ctx, "pub fn get_field_types(index: u16) -> {capnp}::introspect::Type {{" )), indent(body), Line("}".into()), ])) } else { let params = node_reader.parameters_texts(ctx); Ok(Branch(vec![ Line(fmt!( ctx, "pub fn get_field_types<{0}>(index: u16) -> {capnp}::introspect::Type {1} {{", params.params, params.where_clause )), indent(body), Line("}".into()), ])) } } fn annotation_branch( ctx: &GeneratorContext, annotation: schema_capnp::annotation::Reader, child_index: Option, index: u32, ) -> ::capnp::Result { use capnp::schema_capnp::node; let id = annotation.get_id(); let annotation_decl = ctx.node_map[&id]; let node::Annotation(a) = annotation_decl.which()? else { return Err(Error::failed("not an annotation node".into())); }; if annotation_decl.get_is_generic() { let brand = annotation.get_brand()?; let the_mod = ctx.get_qualified_module(id); let func = do_branding(ctx, id, brand, Leaf::GetType, &the_mod)?; Ok(Line(format!("({child_index:?}, {index}) => {}(),", func))) } else { // Avoid referring to the annotation in the generated code, so that users can import // annotation schemas like `c++.capnp` or `rust.capnp` without needing to generate code // for them, as long as the annotations are not generic. let ty = a.get_type()?; Ok(Line(fmt!( ctx, "({child_index:?}, {index}) => <{} as {capnp}::introspect::Introspect>::introspect(),", ty.type_string(ctx, Leaf::Owned)? ))) } } fn generate_get_annotation_types( ctx: &GeneratorContext, node_reader: schema_capnp::node::Reader, ) -> ::capnp::Result { use capnp::schema_capnp::node; let mut branches = vec![]; for (idx, annotation) in node_reader.get_annotations()?.iter().enumerate() { branches.push(annotation_branch(ctx, annotation, None, idx as u32)?); } match node_reader.which()? { node::Struct(s) => { for (fidx, field) in s.get_fields()?.iter().enumerate() { for (idx, annotation) in field.get_annotations()?.iter().enumerate() { branches.push(annotation_branch( ctx, annotation, Some(fidx as u16), idx as u32, )?); } } } node::Enum(e) => { for (fidx, enumerant) in e.get_enumerants()?.iter().enumerate() { for (idx, annotation) in enumerant.get_annotations()?.iter().enumerate() { branches.push(annotation_branch( ctx, annotation, Some(fidx as u16), idx as u32, )?); } } } _ => (), } let body = if branches.is_empty() { Line("panic!(\"invalid annotation indices ({:?}, {}) \", child_index, index)".into()) } else { branches.push(Line( "_ => panic!(\"invalid annotation indices ({:?}, {}) \", child_index, index),".into(), )); indent(vec![ Line("match (child_index, index) {".into()), indent(branches), Line("}".into()), ]) }; if !node_reader.get_is_generic() { Ok(Branch(vec![ Line(fmt!(ctx,"pub fn get_annotation_types(child_index: Option, index: u32) -> {capnp}::introspect::Type {{")), indent(body), Line("}".into()), ])) } else { let params = node_reader.parameters_texts(ctx); Ok(Branch(vec![ Line(fmt!(ctx, "pub fn get_annotation_types<{0}>(child_index: Option, index: u32) -> {capnp}::introspect::Type {1} {{", params.params, params.where_clause )), indent(body), Line("}".into()), ])) } } fn generate_members_by_discriminant( node_reader: schema_capnp::node::Reader, ) -> ::capnp::Result { use capnp::schema_capnp::field; let st = match node_reader.which()? { schema_capnp::node::Struct(st) => st, _ => return Err(Error::failed("not a struct".into())), }; let mut union_member_indexes = vec![]; let mut nonunion_member_indexes = vec![]; for (index, field) in st.get_fields()?.iter().enumerate() { let disc = field.get_discriminant_value(); if disc == field::NO_DISCRIMINANT { nonunion_member_indexes.push(index); } else { union_member_indexes.push((disc, index)); } } union_member_indexes.sort(); let mut nonunion_string: String = "pub static NONUNION_MEMBERS : &[u16] = &[".into(); for idx in 0..nonunion_member_indexes.len() { nonunion_string += &format!("{}", nonunion_member_indexes[idx]); if idx + 1 < nonunion_member_indexes.len() { nonunion_string += ","; } } nonunion_string += "];"; let mut members_by_disc: String = "pub static MEMBERS_BY_DISCRIMINANT : &[u16] = &[".into(); for idx in 0..union_member_indexes.len() { let (disc, index) = union_member_indexes[idx]; assert_eq!(idx, disc as usize); members_by_disc += &format!("{index}"); if idx + 1 < union_member_indexes.len() { members_by_disc += ","; } } members_by_disc += "];"; Ok(Branch(vec![Line(nonunion_string), Line(members_by_disc)])) } fn generate_members_by_name( node_reader: schema_capnp::node::Reader, ) -> ::capnp::Result { let st = match node_reader.which()? { schema_capnp::node::Struct(st) => st, _ => return Err(Error::failed("not a struct".into())), }; let mut members_by_name = Vec::new(); for (index, field) in st.get_fields()?.iter().enumerate() { if let Ok(name) = get_field_name(field) { members_by_name.push((name, index)); } } members_by_name.sort_by_key(|k| k.0); let mut members_by_name_string: String = "pub static MEMBERS_BY_NAME : &[u16] = &[".into(); for (i, (_, index)) in members_by_name.iter().enumerate() { members_by_name_string += &format!("{}", *index); if i + 1 < members_by_name.len() { members_by_name_string += ","; } } members_by_name_string += "];"; Ok(Branch(vec![Line(members_by_name_string)])) } // We need this to work around the fact that Rust does not allow typedefs // with unused type parameters. fn get_ty_params_of_brand( ctx: &GeneratorContext, brand: schema_capnp::brand::Reader, ) -> ::capnp::Result { let mut acc = HashSet::new(); get_ty_params_of_brand_helper(ctx, &mut acc, brand)?; let mut result = String::new(); for (scope_id, parameter_index) in acc.into_iter() { let node = ctx.node_map[&scope_id]; let p = node.get_parameters()?.get(u32::from(parameter_index)); result.push_str(p.get_name()?.to_str()?); result.push(','); } Ok(result) } fn get_ty_params_of_type_helper( ctx: &GeneratorContext, accumulator: &mut HashSet<(u64, u16)>, typ: schema_capnp::type_::Reader, ) -> ::capnp::Result<()> { use capnp::schema_capnp::type_; match typ.which()? { type_::Void(()) | type_::Bool(()) | type_::Int8(()) | type_::Int16(()) | type_::Int32(()) | type_::Int64(()) | type_::Uint8(()) | type_::Uint16(()) | type_::Uint32(()) | type_::Uint64(()) | type_::Float32(()) | type_::Float64(()) | type_::Text(_) | type_::Data(_) => {} type_::AnyPointer(p) => { match p.which()? { type_::any_pointer::Unconstrained(_) => (), type_::any_pointer::Parameter(p) => { accumulator.insert((p.get_scope_id(), p.get_parameter_index())); } type_::any_pointer::ImplicitMethodParameter(_) => { // XXX } } } type_::List(list) => { get_ty_params_of_type_helper(ctx, accumulator, list.get_element_type()?)? } type_::Enum(e) => { get_ty_params_of_brand_helper(ctx, accumulator, e.get_brand()?)?; } type_::Struct(s) => { get_ty_params_of_brand_helper(ctx, accumulator, s.get_brand()?)?; } type_::Interface(interf) => { get_ty_params_of_brand_helper(ctx, accumulator, interf.get_brand()?)?; } } Ok(()) } fn get_ty_params_of_brand_helper( ctx: &GeneratorContext, accumulator: &mut HashSet<(u64, u16)>, brand: schema_capnp::brand::Reader, ) -> ::capnp::Result<()> { for scope in brand.get_scopes()? { let scope_id = scope.get_scope_id(); match scope.which()? { schema_capnp::brand::scope::Bind(bind) => { for binding in bind? { match binding.which()? { schema_capnp::brand::binding::Unbound(()) => {} schema_capnp::brand::binding::Type(t) => { get_ty_params_of_type_helper(ctx, accumulator, t?)? } } } } schema_capnp::brand::scope::Inherit(()) => { let parameters = ctx.node_map[&scope_id].get_parameters()?; for idx in 0..parameters.len() { accumulator.insert((scope_id, idx as u16)); } } } } Ok(()) } fn generate_node( ctx: &GeneratorContext, node_id: u64, node_name: &str, ) -> ::capnp::Result { use capnp::schema_capnp::*; let mut output: Vec = Vec::new(); let mut nested_output: Vec = Vec::new(); let node_reader = &ctx.node_map[&node_id]; let nested_nodes = node_reader.get_nested_nodes()?; for nested_node in nested_nodes { let id = nested_node.get_id(); nested_output.push(generate_node(ctx, id, ctx.get_last_name(id)?)?); } match node_reader.which()? { node::File(()) => { output.push(Branch(nested_output)); } node::Struct(struct_reader) => { let params = node_reader.parameters_texts(ctx); output.push(BlankLine); let is_generic = node_reader.get_is_generic(); if is_generic { output.push(Line(format!( "pub mod {} {{ /* {} */", node_name, params.expanded_list.join(",") ))); } else { output.push(Line(format!("pub mod {node_name} {{"))); } let bracketed_params = if params.params.is_empty() { "".to_string() } else { format!("<{}>", params.params) }; let mut preamble = Vec::new(); let mut builder_members = Vec::new(); let mut reader_members = Vec::new(); let mut union_fields = Vec::new(); let mut which_enums = Vec::new(); let mut pipeline_impl_interior = Vec::new(); let mut private_mod_interior = Vec::new(); let data_size = struct_reader.get_data_word_count(); let pointer_size = struct_reader.get_pointer_count(); let discriminant_count = struct_reader.get_discriminant_count(); let discriminant_offset = struct_reader.get_discriminant_offset(); private_mod_interior.push(crate::pointer_constants::node_word_array_declaration( ctx, "ENCODED_NODE", *node_reader, crate::pointer_constants::WordArrayDeclarationOptions { public: true }, )?); private_mod_interior.push(generate_get_field_types(ctx, *node_reader)?); private_mod_interior.push(generate_get_annotation_types(ctx, *node_reader)?); // `static` instead of `const` so that this has a fixed memory address // and we can check equality of `RawStructSchema` values by comparing pointers. private_mod_interior.push(Branch(vec![ Line(fmt!(ctx,"pub static RAW_SCHEMA: {capnp}::introspect::RawStructSchema = {capnp}::introspect::RawStructSchema {{")), indent(vec![ Line("encoded_node: &ENCODED_NODE,".into()), Line("nonunion_members: NONUNION_MEMBERS,".into()), Line("members_by_discriminant: MEMBERS_BY_DISCRIMINANT,".into()), Line("members_by_name: MEMBERS_BY_NAME,".into()), ]), Line("};".into()), ])); private_mod_interior.push(generate_members_by_discriminant(*node_reader)?); private_mod_interior.push(generate_members_by_name(*node_reader)?); let fields = struct_reader.get_fields()?; for field in fields { let name = get_field_name(field)?; let styled_name = camel_to_snake_case(name); let discriminant_value = field.get_discriminant_value(); let is_union_field = discriminant_value != field::NO_DISCRIMINANT; if !is_union_field { pipeline_impl_interior.push(generate_pipeline_getter(ctx, field)?); let (ty, get, default_decl) = getter_text(ctx, &field, true, true)?; if let Some(default) = default_decl { private_mod_interior.push(default.clone()); } reader_members.push(Branch(vec![ line("#[inline]"), Line(format!("pub fn get_{styled_name}(self) {ty} {{")), indent(get), line("}"), ])); let (ty_b, get_b, _) = getter_text(ctx, &field, false, true)?; builder_members.push(Branch(vec![ line("#[inline]"), Line(format!("pub fn get_{styled_name}(self) {ty_b} {{")), indent(get_b), line("}"), ])); } else { union_fields.push(field); } builder_members.push(generate_setter( ctx, discriminant_offset, &styled_name, &field, )?); reader_members.push(generate_haser( discriminant_offset, &styled_name, &field, true, )?); builder_members.push(generate_haser( discriminant_offset, &styled_name, &field, false, )?); if let Ok(field::Group(group)) = field.which() { let id = group.get_type_id(); let text = generate_node(ctx, id, ctx.get_last_name(id)?)?; nested_output.push(text); } } if discriminant_count > 0 { let (which_enums1, union_getter, typedef, mut default_decls) = generate_union(ctx, discriminant_offset, &union_fields, true, ¶ms)?; which_enums.push(which_enums1); which_enums.push(typedef); reader_members.push(union_getter); private_mod_interior.append(&mut default_decls); let (_, union_getter, typedef, _) = generate_union(ctx, discriminant_offset, &union_fields, false, ¶ms)?; which_enums.push(typedef); builder_members.push(union_getter); let mut reexports = String::new(); reexports.push_str("pub use self::Which::{"); let mut whichs = Vec::new(); for f in &union_fields { whichs.push(capitalize_first_letter(get_field_name(*f)?)); } reexports.push_str(&whichs.join(",")); reexports.push_str("};"); preamble.push(Line(reexports)); preamble.push(BlankLine); } let builder_struct_size = Branch(vec![ Line(fmt!(ctx,"impl <'a,{0}> {capnp}::traits::HasStructSize for Builder<'a,{0}> {1} {{", params.params, params.where_clause)), indent(Line( fmt!(ctx,"const STRUCT_SIZE: {capnp}::private::layout::StructSize = {capnp}::private::layout::StructSize {{ data: {}, pointers: {} }};", data_size as usize, pointer_size as usize))), line("}")]); private_mod_interior.push(Line(format!( "pub const TYPE_ID: u64 = {};", format_u64(node_id) ))); let from_pointer_builder_impl = Branch(vec![ Line(fmt!(ctx,"impl <'a,{0}> {capnp}::traits::FromPointerBuilder<'a> for Builder<'a,{0}> {1} {{", params.params, params.where_clause)), indent(vec![ Line(fmt!(ctx,"fn init_pointer(builder: {capnp}::private::layout::PointerBuilder<'a>, _size: u32) -> Self {{")), indent(Line(fmt!(ctx,"builder.init_struct(::STRUCT_SIZE).into()"))), line("}"), Line(fmt!(ctx,"fn get_from_pointer(builder: {capnp}::private::layout::PointerBuilder<'a>, default: ::core::option::Option<&'a [{capnp}::Word]>) -> {capnp}::Result {{")), indent(Line(fmt!(ctx,"::core::result::Result::Ok(builder.get_struct(::STRUCT_SIZE, default)?.into())"))), line("}") ]), line("}"), BlankLine]); let accessors = vec![ Branch(preamble), (if !is_generic { Branch(vec![ Line("#[derive(Copy, Clone)]".into()), line("pub struct Owned(());"), 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 }}).into() }} }}")), Line(fmt!(ctx, "impl {capnp}::traits::Owned for Owned {{ type Reader<'a> = Reader<'a>; type Builder<'a> = Builder<'a>; }}")), Line(fmt!(ctx,"impl {capnp}::traits::OwnedStruct for Owned {{ type Reader<'a> = Reader<'a>; type Builder<'a> = Builder<'a>; }}")), Line(fmt!(ctx,"impl {capnp}::traits::Pipelined for Owned {{ type Pipeline = Pipeline; }}")) ]) } else { Branch(vec![ Line("#[derive(Copy, Clone)]".into()), Line(format!("pub struct Owned<{}> {{", params.params)), indent(Line(params.phantom_data_type.clone())), line("}"), 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}> }}).into() }} }}", params.params, params.where_clause)), Line(fmt!(ctx,"impl <{0}> {capnp}::traits::Owned for Owned <{0}> {1} {{ type Reader<'a> = Reader<'a, {0}>; type Builder<'a> = Builder<'a, {0}>; }}", params.params, params.where_clause)), Line(fmt!(ctx,"impl <{0}> {capnp}::traits::OwnedStruct for Owned <{0}> {1} {{ type Reader<'a> = Reader<'a, {0}>; type Builder<'a> = Builder<'a, {0}>; }}", params.params, params.where_clause)), Line(fmt!(ctx,"impl <{0}> {capnp}::traits::Pipelined for Owned<{0}> {1} {{ type Pipeline = Pipeline{2}; }}", params.params, params.where_clause, bracketed_params)), ]) }), BlankLine, (if !is_generic { Line(fmt!(ctx,"pub struct Reader<'a> {{ reader: {capnp}::private::layout::StructReader<'a> }}")) } else { Branch(vec![ Line(format!("pub struct Reader<'a,{}> {} {{", params.params, params.where_clause)), indent(vec![ Line(fmt!(ctx,"reader: {capnp}::private::layout::StructReader<'a>,")), Line(params.phantom_data_type.clone()), ]), line("}") ]) }), // Manually implement Copy/Clone because `derive` only kicks in if all of // the parameters are known to implement Copy/Clone. Branch(vec![ Line(format!("impl <'a,{0}> ::core::marker::Copy for Reader<'a,{0}> {1} {{}}", params.params, params.where_clause)), Line(format!("impl <'a,{0}> ::core::clone::Clone for Reader<'a,{0}> {1} {{", params.params, params.where_clause)), indent(Line("fn clone(&self) -> Self { *self }".into())), Line("}".into())]), BlankLine, Branch(vec![ Line(fmt!(ctx,"impl <'a,{0}> {capnp}::traits::HasTypeId for Reader<'a,{0}> {1} {{", params.params, params.where_clause)), indent(vec![line("const TYPE_ID: u64 = _private::TYPE_ID;")]), line("}")]), Line(fmt!(ctx,"impl <'a,{0}> ::core::convert::From<{capnp}::private::layout::StructReader<'a>> for Reader<'a,{0}> {1} {{", params.params, params.where_clause)), indent(vec![ Line(fmt!(ctx,"fn from(reader: {capnp}::private::layout::StructReader<'a>) -> Self {{")), indent(Line(format!("Self {{ reader, {} }}", params.phantom_data_value))), line("}") ]), line("}"), BlankLine, Line(fmt!(ctx,"impl <'a,{0}> ::core::convert::From> for {capnp}::dynamic_value::Reader<'a> {1} {{", params.params, params.where_clause)), indent(vec![ Line(format!("fn from(reader: Reader<'a,{0}>) -> Self {{", params.params)), 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}>}})))", params.params))), line("}") ]), line("}"), BlankLine, Line(format!("impl <'a,{0}> ::core::fmt::Debug for Reader<'a,{0}> {1} {{", params.params, params.where_clause)), indent(vec![ Line("fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::result::Result<(), ::core::fmt::Error> {".into()), indent(Line(fmt!(ctx,"core::fmt::Debug::fmt(&::core::convert::Into::<{capnp}::dynamic_value::Reader<'_>>::into(*self), f)"))), line("}") ]), line("}"), BlankLine, Line(fmt!(ctx,"impl <'a,{0}> {capnp}::traits::FromPointerReader<'a> for Reader<'a,{0}> {1} {{", params.params, params.where_clause)), indent(vec![ Line(fmt!(ctx,"fn get_from_pointer(reader: &{capnp}::private::layout::PointerReader<'a>, default: ::core::option::Option<&'a [{capnp}::Word]>) -> {capnp}::Result {{")), indent(line("::core::result::Result::Ok(reader.get_struct(default)?.into())")), line("}") ]), line("}"), BlankLine, Line(fmt!(ctx,"impl <'a,{0}> {capnp}::traits::IntoInternalStructReader<'a> for Reader<'a,{0}> {1} {{", params.params, params.where_clause)), indent(vec![ Line(fmt!(ctx,"fn into_internal_struct_reader(self) -> {capnp}::private::layout::StructReader<'a> {{")), indent(line("self.reader")), line("}") ]), line("}"), BlankLine, Line(fmt!(ctx,"impl <'a,{0}> {capnp}::traits::Imbue<'a> for Reader<'a,{0}> {1} {{", params.params, params.where_clause)), indent(vec![ Line(fmt!(ctx,"fn imbue(&mut self, cap_table: &'a {capnp}::private::layout::CapTable) {{")), indent(Line(fmt!(ctx,"self.reader.imbue({capnp}::private::layout::CapTableReader::Plain(cap_table))"))), line("}") ]), line("}"), BlankLine, Line(format!("impl <'a,{0}> Reader<'a,{0}> {1} {{", params.params, params.where_clause)), indent(vec![ Line(format!("pub fn reborrow(&self) -> Reader<'_,{}> {{",params.params)), indent(line("Self { .. *self }")), line("}"), BlankLine, Line(fmt!(ctx,"pub fn total_size(&self) -> {capnp}::Result<{capnp}::MessageSize> {{")), indent(line("self.reader.total_size()")), line("}")]), indent(reader_members), line("}"), BlankLine, (if !is_generic { Line(fmt!(ctx,"pub struct Builder<'a> {{ builder: {capnp}::private::layout::StructBuilder<'a> }}")) } else { Branch(vec![ Line(format!("pub struct Builder<'a,{}> {} {{", params.params, params.where_clause)), indent(vec![ Line(fmt!(ctx, "builder: {capnp}::private::layout::StructBuilder<'a>,")), Line(params.phantom_data_type.clone()), ]), line("}") ]) }), builder_struct_size, Branch(vec![ Line(fmt!(ctx,"impl <'a,{0}> {capnp}::traits::HasTypeId for Builder<'a,{0}> {1} {{", params.params, params.where_clause)), indent(vec![ line("const TYPE_ID: u64 = _private::TYPE_ID;")]), line("}") ]), Line(fmt!(ctx, "impl <'a,{0}> ::core::convert::From<{capnp}::private::layout::StructBuilder<'a>> for Builder<'a,{0}> {1} {{", params.params, params.where_clause)), indent(vec![ Line(fmt!(ctx,"fn from(builder: {capnp}::private::layout::StructBuilder<'a>) -> Self {{")), indent(Line(format!("Self {{ builder, {} }}", params.phantom_data_value))), line("}") ]), line("}"), BlankLine, Line(fmt!(ctx,"impl <'a,{0}> ::core::convert::From> for {capnp}::dynamic_value::Builder<'a> {1} {{", params.params, params.where_clause)), indent(vec![ Line(format!("fn from(builder: Builder<'a,{0}>) -> Self {{", params.params)), 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}>}})))", params.params))), line("}") ]), line("}"), BlankLine, Line(fmt!(ctx,"impl <'a,{0}> {capnp}::traits::ImbueMut<'a> for Builder<'a,{0}> {1} {{", params.params, params.where_clause)), indent(vec![ Line(fmt!(ctx,"fn imbue_mut(&mut self, cap_table: &'a mut {capnp}::private::layout::CapTable) {{")), indent(Line(fmt!(ctx,"self.builder.imbue({capnp}::private::layout::CapTableBuilder::Plain(cap_table))"))), line("}")]), line("}"), BlankLine, from_pointer_builder_impl, Line(fmt!(ctx, "impl <'a,{0}> {capnp}::traits::SetterInput> for Reader<'a,{0}> {1} {{", params.params, params.where_clause)), 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) }}"))), line("}"), BlankLine, Line(format!("impl <'a,{0}> Builder<'a,{0}> {1} {{", params.params, params.where_clause)), indent(vec![ Line(format!("pub fn into_reader(self) -> Reader<'a,{}> {{", params.params)), indent(line("self.builder.into_reader().into()")), line("}"), Line(format!("pub fn reborrow(&mut self) -> Builder<'_,{}> {{", params.params)), (if !is_generic { indent(line("Builder { builder: self.builder.reborrow() }")) } else { indent(line("Builder { builder: self.builder.reborrow(), ..*self }")) }), line("}"), Line(format!("pub fn reborrow_as_reader(&self) -> Reader<'_,{}> {{", params.params)), indent(line("self.builder.as_reader().into()")), line("}"), BlankLine, Line(fmt!(ctx,"pub fn total_size(&self) -> {capnp}::Result<{capnp}::MessageSize> {{")), indent(line("self.builder.as_reader().total_size()")), line("}") ]), indent(builder_members), line("}"), BlankLine, (if is_generic { Branch(vec![ Line(format!("pub struct Pipeline{bracketed_params} {{")), indent(vec![ Line(fmt!(ctx,"_typeless: {capnp}::any_pointer::Pipeline,")), Line(params.phantom_data_type), ]), line("}") ]) } else { Line(fmt!(ctx,"pub struct Pipeline {{ _typeless: {capnp}::any_pointer::Pipeline }}")) }), Line(fmt!(ctx,"impl{bracketed_params} {capnp}::capability::FromTypelessPipeline for Pipeline{bracketed_params} {{")), indent(vec![ Line(fmt!(ctx,"fn new(typeless: {capnp}::any_pointer::Pipeline) -> Self {{")), indent(Line(format!("Self {{ _typeless: typeless, {} }}", params.phantom_data_value))), line("}")]), line("}"), Line(format!("impl{0} Pipeline{0} {1} {{", bracketed_params, params.pipeline_where_clause)), indent(pipeline_impl_interior), line("}"), line("mod _private {"), indent(private_mod_interior), line("}"), ]; output.push(indent(vec![ Branch(accessors), Branch(which_enums), Branch(nested_output), ])); output.push(line("}")); } node::Enum(enum_reader) => { let last_name = ctx.get_last_name(node_id)?; let name_as_mod = module_name(last_name); output.push(BlankLine); let mut members = Vec::new(); let mut match_branches = Vec::new(); let enumerants = enum_reader.get_enumerants()?; for (ii, enumerant) in enumerants.into_iter().enumerate() { let enumerant = capitalize_first_letter(get_enumerant_name(enumerant)?); members.push(Line(format!("{enumerant} = {ii},"))); match_branches.push(Line(format!( "{ii} => ::core::result::Result::Ok(Self::{enumerant})," ))); } match_branches.push(Line(fmt!( ctx, "n => ::core::result::Result::Err({capnp}::NotInSchema(n))," ))); output.push(Branch(vec![ line("#[repr(u16)]"), line("#[derive(Clone, Copy, Debug, PartialEq, Eq)]"), Line(format!("pub enum {last_name} {{")), indent(members), line("}"), ])); output.push(BlankLine); output.push(Branch(vec![ Line(fmt!(ctx, "impl {capnp}::introspect::Introspect for {last_name} {{" )), indent(Line(fmt!(ctx, "fn introspect() -> {capnp}::introspect::Type {{ {capnp}::introspect::TypeVariant::Enum({capnp}::introspect::RawEnumSchema {{ encoded_node: &{0}::ENCODED_NODE, annotation_types: {0}::get_annotation_types }}).into() }}", name_as_mod))), Line("}".into()), ])); output.push(Branch(vec![ Line(fmt!(ctx,"impl <'a> ::core::convert::From<{last_name}> for {capnp}::dynamic_value::Reader<'a> {{")), indent(Line(fmt!(ctx, "fn from(e: {last_name}) -> Self {{ {capnp}::dynamic_value::Enum::new(e.into(), {capnp}::introspect::RawEnumSchema {{ encoded_node: &{0}::ENCODED_NODE, annotation_types: {0}::get_annotation_types }}.into()).into() }}", name_as_mod ))), Line("}".into()) ])); output.push(Branch(vec![ Line(format!( "impl ::core::convert::TryFrom for {last_name} {{" )), indent(Line( fmt!(ctx,"type Error = {capnp}::NotInSchema;"), )), indent(vec![ Line( format!("fn try_from(value: u16) -> ::core::result::Result>::Error> {{") ), indent(vec![ line("match value {"), indent(match_branches), line("}"), ]), line("}"), ]), line("}"), Line(format!("impl From<{last_name}> for u16 {{")), indent(line("#[inline]")), indent(Line(format!( "fn from(x: {last_name}) -> u16 {{ x as u16 }}" ))), line("}"), ])); output.push(Branch(vec![ Line(fmt!( ctx, "impl {capnp}::traits::HasTypeId for {last_name} {{" )), indent(Line(format!( "const TYPE_ID: u64 = {}u64;", format_u64(node_id) ))), line("}"), ])); output.push(Branch(vec![ Line(format!("mod {} {{", name_as_mod)), Branch(vec![ crate::pointer_constants::node_word_array_declaration( ctx, "ENCODED_NODE", *node_reader, crate::pointer_constants::WordArrayDeclarationOptions { public: true }, )?, generate_get_annotation_types(ctx, *node_reader)?, ]), Line("}".into()), ])); } node::Interface(interface) => { let params = node_reader.parameters_texts(ctx); output.push(BlankLine); let is_generic = node_reader.get_is_generic(); let names = &ctx.scope_map[&node_id]; let mut client_impl_interior = Vec::new(); let mut server_interior = Vec::new(); let mut mod_interior = Vec::new(); let mut dispatch_arms = Vec::new(); let mut private_mod_interior = Vec::new(); let bracketed_params = if params.params.is_empty() { "".to_string() } else { format!("<{}>", params.params) }; private_mod_interior.push(Line(format!( "pub const TYPE_ID: u64 = {};", format_u64(node_id) ))); mod_interior.push(line("#![allow(unused_variables)]")); let methods = interface.get_methods()?; for (ordinal, method) in methods.into_iter().enumerate() { let name = method.get_name()?.to_str()?; let param_id = method.get_param_struct_type(); let param_node = &ctx.node_map[¶m_id]; let (param_scopes, params_ty_params) = if param_node.get_scope_id() == 0 { let mut names = names.clone(); let local_name = module_name(&format!("{name}Params")); nested_output.push(generate_node(ctx, param_id, &local_name)?); names.push(local_name); (names, params.params.clone()) } else { ( ctx.scope_map[¶m_node.get_id()].clone(), get_ty_params_of_brand(ctx, method.get_param_brand()?)?, ) }; let param_type = do_branding( ctx, param_id, method.get_param_brand()?, Leaf::Owned, ¶m_scopes.join("::"), )?; let result_id = method.get_result_struct_type(); let result_node = &ctx.node_map[&result_id]; let (result_scopes, results_ty_params) = if result_node.get_scope_id() == 0 { let mut names = names.clone(); let local_name = module_name(&format!("{name}Results")); nested_output.push(generate_node(ctx, result_id, &local_name)?); names.push(local_name); (names, params.params.clone()) } else { ( ctx.scope_map[&result_node.get_id()].clone(), get_ty_params_of_brand(ctx, method.get_result_brand()?)?, ) }; let result_type = do_branding( ctx, result_id, method.get_result_brand()?, Leaf::Owned, &result_scopes.join("::"), )?; dispatch_arms.push( Line(fmt!(ctx, "{ordinal} => server.{}({capnp}::private::capability::internal_get_typed_params(params), {capnp}::private::capability::internal_get_typed_results(results)),", module_name(name)))); mod_interior.push(Line(fmt!( ctx, "pub type {}Params<{}> = {capnp}::capability::Params<{}>;", capitalize_first_letter(name), params_ty_params, param_type ))); mod_interior.push(Line(fmt!( ctx, "pub type {}Results<{}> = {capnp}::capability::Results<{}>;", capitalize_first_letter(name), results_ty_params, result_type ))); server_interior.push( Line(fmt!(ctx, "fn {}(&mut self, _: {}Params<{}>, _: {}Results<{}>) -> {capnp}::capability::Promise<(), {capnp}::Error> {{ {capnp}::capability::Promise::err({capnp}::Error::unimplemented(\"method {}::Server::{} not implemented\".to_string())) }}", module_name(name), capitalize_first_letter(name), params_ty_params, capitalize_first_letter(name), results_ty_params, node_name, module_name(name) ))); client_impl_interior.push(Line(fmt!( ctx, "pub fn {}_request(&self) -> {capnp}::capability::Request<{},{}> {{", camel_to_snake_case(name), param_type, result_type ))); client_impl_interior.push(indent(Line(format!( "self.client.new_call(_private::TYPE_ID, {ordinal}, ::core::option::Option::None)" )))); client_impl_interior.push(line("}")); method.get_annotations()?; } let mut base_dispatch_arms = Vec::new(); let server_base = { let mut base_traits = Vec::new(); fn find_super_interfaces<'a>( interface: schema_capnp::node::interface::Reader<'a>, all_extends: &mut Vec< ::Reader<'a>, >, ctx: &GeneratorContext<'a>, ) -> ::capnp::Result<()> { let extends = interface.get_superclasses()?; for superclass in extends { if let node::Interface(interface) = ctx.node_map[&superclass.get_id()].which()? { find_super_interfaces(interface, all_extends, ctx)?; } all_extends.push(superclass); } Ok(()) } let mut extends = Vec::new(); find_super_interfaces(interface, &mut extends, ctx)?; for interface in &extends { let type_id = interface.get_id(); let brand = interface.get_brand()?; let the_mod = ctx.get_qualified_module(type_id); base_dispatch_arms.push(Line(format!( "0x{type_id:x} => {}::dispatch_call_internal(&mut self.server, method_id, params, results),", do_branding( ctx, type_id, brand, Leaf::ServerDispatch, &the_mod)?))); base_traits.push(do_branding(ctx, type_id, brand, Leaf::Server, &the_mod)?); } if !extends.is_empty() { format!(": {}", base_traits.join(" + ")) } else { "".to_string() } }; mod_interior.push(BlankLine); mod_interior.push(Line(format!("pub struct Client{bracketed_params} {{"))); mod_interior.push(indent(Line(fmt!( ctx, "pub client: {capnp}::capability::Client," )))); if is_generic { mod_interior.push(indent(Line(params.phantom_data_type.clone()))); } mod_interior.push(line("}")); mod_interior.push( Branch(vec![ Line(fmt!(ctx,"impl {bracketed_params} {capnp}::capability::FromClientHook for Client{bracketed_params} {{")), indent(Line(fmt!(ctx,"fn new(hook: Box) -> Self {{"))), indent(indent(Line(fmt!(ctx,"Self {{ client: {capnp}::capability::Client::new(hook), {} }}", params.phantom_data_value)))), indent(line("}")), indent(Line(fmt!(ctx,"fn into_client_hook(self) -> Box {{"))), indent(indent(line("self.client.hook"))), indent(line("}")), indent(Line(fmt!(ctx,"fn as_client_hook(&self) -> &dyn ({capnp}::private::capability::ClientHook) {{"))), indent(indent(line("&*self.client.hook"))), indent(line("}")), line("}")])); mod_interior.push(if !is_generic { Branch(vec![ Line("#[derive(Copy, Clone)]".into()), line("pub struct Owned(());"), Line(fmt!(ctx,"impl {capnp}::introspect::Introspect for Owned {{ fn introspect() -> {capnp}::introspect::Type {{ {capnp}::introspect::TypeVariant::Capability.into() }} }}")), line("impl ::capnp::traits::Owned for Owned { type Reader<'a> = Client; type Builder<'a> = Client; }"), Line(fmt!(ctx,"impl {capnp}::traits::Pipelined for Owned {{ type Pipeline = Client; }}"))]) } else { Branch(vec![ Line("#[derive(Copy, Clone)]".into()), Line(format!("pub struct Owned<{}> {} {{", params.params, params.where_clause)), indent(Line(params.phantom_data_type.clone())), line("}"), Line(fmt!(ctx, "impl <{0}> {capnp}::introspect::Introspect for Owned <{0}> {1} {{ fn introspect() -> {capnp}::introspect::Type {{ {capnp}::introspect::TypeVariant::Capability.into() }} }}", params.params, params.where_clause)), Line(fmt!(ctx, "impl <{0}> {capnp}::traits::Owned for Owned <{0}> {1} {{ type Reader<'a> = Client<{0}>; type Builder<'a> = Client<{0}>; }}", params.params, params.where_clause)), Line(fmt!(ctx, "impl <{0}> {capnp}::traits::Pipelined for Owned <{0}> {1} {{ type Pipeline = Client{2}; }}", params.params, params.where_clause, bracketed_params))]) }); mod_interior.push(Branch(vec![ Line(fmt!(ctx,"impl <'a,{0}> {capnp}::traits::FromPointerReader<'a> for Client<{0}> {1} {{", params.params, params.where_clause)), indent(vec![ Line(fmt!(ctx,"fn get_from_pointer(reader: &{capnp}::private::layout::PointerReader<'a>, _default: ::core::option::Option<&'a [{capnp}::Word]>) -> {capnp}::Result {{")), indent(Line(fmt!(ctx,"::core::result::Result::Ok({capnp}::capability::FromClientHook::new(reader.get_capability()?))"))), line("}")]), line("}")])); mod_interior.push(Branch(vec![ Line(fmt!(ctx,"impl <'a,{0}> {capnp}::traits::FromPointerBuilder<'a> for Client<{0}> {1} {{", params.params, params.where_clause)), indent(vec![ Line(fmt!(ctx,"fn init_pointer(_builder: {capnp}::private::layout::PointerBuilder<'a>, _size: u32) -> Self {{")), indent(line("unimplemented!()")), line("}"), Line(fmt!(ctx,"fn get_from_pointer(builder: {capnp}::private::layout::PointerBuilder<'a>, _default: ::core::option::Option<&'a [{capnp}::Word]>) -> {capnp}::Result {{")), indent(Line(fmt!(ctx,"::core::result::Result::Ok({capnp}::capability::FromClientHook::new(builder.get_capability()?))"))), line("}")]), line("}"), BlankLine])); mod_interior.push(Branch(vec![ Line(fmt!(ctx, "impl <{0}> {capnp}::traits::SetterInput> for Client<{0}> {1} {{", params.params, params.where_clause)), indent(vec![ Line(fmt!(ctx,"fn set_pointer_builder(mut pointer: {capnp}::private::layout::PointerBuilder<'_>, from: Self, _canonicalize: bool) -> {capnp}::Result<()> {{")), indent(Line("pointer.set_capability(from.client.hook);".to_string())), indent(Line("::core::result::Result::Ok(())".to_string())), line("}") ] ), line("}")])); mod_interior.push(Branch(vec![ Line(fmt!(ctx, "impl {bracketed_params} {capnp}::traits::HasTypeId for Client{bracketed_params} {{" )), indent(Line( "const TYPE_ID: u64 = _private::TYPE_ID;".to_string(), )), line("}"), ])); mod_interior.push( Branch(vec![ Line(format!("impl {bracketed_params} Clone for Client{bracketed_params} {{")), indent(line("fn clone(&self) -> Self {")), indent(indent(Line(fmt!(ctx,"Self {{ client: {capnp}::capability::Client::new(self.client.hook.add_ref()), {} }}", params.phantom_data_value)))), indent(line("}")), line("}")])); mod_interior.push(Branch(vec![ Line(format!( "impl {bracketed_params} Client{bracketed_params} {{" )), indent(client_impl_interior), line("}"), ])); mod_interior.push(Branch(vec![ Line(format!( "pub trait Server<{}> {} {} {{", params.params, server_base, params.where_clause )), indent(server_interior), line("}"), ])); mod_interior.push(Branch(vec![ Line(format!( "pub struct ServerDispatch<_T,{}> {{", params.params )), indent(line("pub server: _T,")), indent(if is_generic { vec![Line(params.phantom_data_type.clone())] } else { vec![] }), line("}"), ])); mod_interior.push(Branch(vec![ Line( fmt!(ctx,"impl <_S: Server{1} + 'static, {0}> {capnp}::capability::FromServer<_S> for Client{1} {2} {{", params.params, bracketed_params, params.where_clause_with_static)), indent(vec![ Line(format!("type Dispatch = ServerDispatch<_S, {}>;", params.params)), Line(format!("fn from_server(s: _S) -> ServerDispatch<_S, {}> {{", params.params)), indent(Line(format!("ServerDispatch {{ server: s, {} }}", params.phantom_data_value))), line("}"), ]), line("}"), ])); mod_interior.push( Branch(vec![ (if is_generic { Line(format!("impl <{}, _T: Server{}> ::core::ops::Deref for ServerDispatch<_T,{}> {} {{", params.params, bracketed_params, params.params, params.where_clause)) } else { line("impl <_T: Server> ::core::ops::Deref for ServerDispatch<_T> {") }), indent(line("type Target = _T;")), indent(line("fn deref(&self) -> &_T { &self.server}")), line("}"), ])); mod_interior.push( Branch(vec![ (if is_generic { Line(format!("impl <{}, _T: Server{}> ::core::ops::DerefMut for ServerDispatch<_T,{}> {} {{", params.params, bracketed_params, params.params, params.where_clause)) } else { line("impl <_T: Server> ::core::ops::DerefMut for ServerDispatch<_T> {") }), indent(line("fn deref_mut(&mut self) -> &mut _T { &mut self.server}")), line("}"), ])); mod_interior.push( Branch(vec![ (if is_generic { Line(fmt!(ctx,"impl <{}, _T: Server{}> {capnp}::capability::Server for ServerDispatch<_T,{}> {} {{", params.params, bracketed_params, params.params, params.where_clause)) } else { Line(fmt!(ctx,"impl <_T: Server> {capnp}::capability::Server for ServerDispatch<_T> {{")) }), indent(Line(fmt!(ctx,"fn dispatch_call(&mut 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::Promise<(), {capnp}::Error> {{"))), indent(indent(line("match interface_id {"))), indent(indent(indent(line("_private::TYPE_ID => Self::dispatch_call_internal(&mut self.server, method_id, params, results),")))), indent(indent(indent(base_dispatch_arms))), indent(indent(indent(Line(fmt!(ctx,"_ => {{ {capnp}::capability::Promise::err({capnp}::Error::unimplemented(\"Method not implemented.\".to_string())) }}"))))), indent(indent(line("}"))), indent(line("}")), line("}")])); mod_interior.push( Branch(vec![ (if is_generic { Line(format!("impl <{}, _T: Server{}> ServerDispatch<_T,{}> {} {{", params.params, bracketed_params, params.params, params.where_clause)) } else { line("impl <_T :Server> ServerDispatch<_T> {") }), indent(Line(fmt!(ctx,"pub fn dispatch_call_internal(server: &mut _T, method_id: u16, params: {capnp}::capability::Params<{capnp}::any_pointer::Owned>, results: {capnp}::capability::Results<{capnp}::any_pointer::Owned>) -> {capnp}::capability::Promise<(), {capnp}::Error> {{"))), indent(indent(line("match method_id {"))), indent(indent(indent(dispatch_arms))), indent(indent(indent(Line(fmt!(ctx,"_ => {{ ::capnp::capability::Promise::err({capnp}::Error::unimplemented(\"Method not implemented.\".to_string())) }}"))))), indent(indent(line("}"))), indent(line("}")), line("}")])); mod_interior.push(Branch(vec![ line("pub mod _private {"), indent(private_mod_interior), line("}"), ])); mod_interior.push(Branch(vec![Branch(nested_output)])); output.push(BlankLine); if is_generic { output.push(Line(format!( "pub mod {} {{ /* ({}) */", node_name, params.expanded_list.join(",") ))); } else { output.push(Line(format!("pub mod {node_name} {{"))); } output.push(indent(mod_interior)); output.push(line("}")); } node::Const(c) => { let styled_name = snake_to_upper_case(ctx.get_last_name(node_id)?); let typ = c.get_type()?; let formatted_text = match (typ.which()?, c.get_value()?.which()?) { (type_::Void(()), value::Void(())) => { Line(format!("pub const {styled_name}: () = ();")) } (type_::Bool(()), value::Bool(b)) => { Line(format!("pub const {styled_name}: bool = {b};")) } (type_::Int8(()), value::Int8(i)) => { Line(format!("pub const {styled_name}: i8 = {i};")) } (type_::Int16(()), value::Int16(i)) => { Line(format!("pub const {styled_name}: i16 = {i};")) } (type_::Int32(()), value::Int32(i)) => { Line(format!("pub const {styled_name}: i32 = {i};")) } (type_::Int64(()), value::Int64(i)) => { Line(format!("pub const {styled_name}: i64 = {i};")) } (type_::Uint8(()), value::Uint8(i)) => { Line(format!("pub const {styled_name}: u8 = {i};")) } (type_::Uint16(()), value::Uint16(i)) => { Line(format!("pub const {styled_name}: u16 = {i};")) } (type_::Uint32(()), value::Uint32(i)) => { Line(format!("pub const {styled_name}: u32 = {i};")) } (type_::Uint64(()), value::Uint64(i)) => { Line(format!("pub const {styled_name}: u64 = {i};")) } (type_::Float32(()), value::Float32(f)) => { Line(format!("pub const {styled_name}: f32 = {f:e}f32;")) } (type_::Float64(()), value::Float64(f)) => { Line(format!("pub const {styled_name}: f64 = {f:e}f64;")) } (type_::Enum(e), value::Enum(v)) => { if let Some(node) = ctx.node_map.get(&e.get_type_id()) { match node.which()? { node::Enum(e) => { let enumerants = e.get_enumerants()?; if let Some(enumerant) = enumerants.try_get(u32::from(v)) { let variant = capitalize_first_letter(get_enumerant_name(enumerant)?); let type_string = typ.type_string(ctx, Leaf::Owned)?; Line(format!( "pub const {}: {} = {}::{};", styled_name, &type_string, &type_string, variant )) } else { return Err(Error::failed(format!( "enumerant out of range: {v}" ))); } } _ => { return Err(Error::failed(format!( "bad enum type ID: {}", e.get_type_id() ))); } } } else { return Err(Error::failed(format!( "bad enum type ID: {}", e.get_type_id() ))); } } (type_::Text(()), value::Text(t)) => Line(format!( "pub const {styled_name}: &str = {:?};", t?.to_str()? )), (type_::Data(()), value::Data(d)) => { Line(format!("pub const {styled_name}: &[u8] = &{:?};", d?)) } (type_::List(_), value::List(v)) => { generate_pointer_constant(ctx, &styled_name, typ, v)? } (type_::Struct(_), value::Struct(v)) => { generate_pointer_constant(ctx, &styled_name, typ, v)? } (type_::Interface(_t), value::Interface(())) => { return Err(Error::unimplemented("interface constants".to_string())); } (type_::AnyPointer(_), value::AnyPointer(_pr)) => { return Err(Error::unimplemented("anypointer constants".to_string())); } _ => { return Err(Error::failed("type does not match value".to_string())); } }; output.push(formatted_text); } node::Annotation(annotation_reader) => { let is_generic = node_reader.get_is_generic(); let params = node_reader.parameters_texts(ctx); let last_name = ctx.get_last_name(node_id)?; let mut interior = vec![]; interior.push(Line(format!("pub const ID: u64 = 0x{:x};", node_id))); let ty = annotation_reader.get_type()?; if !is_generic { interior.push(Line(fmt!(ctx, "pub fn get_type() -> {capnp}::introspect::Type {{ <{} as {capnp}::introspect::Introspect>::introspect() }}", ty.type_string(ctx, Leaf::Owned)?))); } else { 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)?))); } output.push(Branch(vec![ Line(format!("pub mod {} {{", last_name)), indent(interior), Line("}".into()), ])); } } Ok(Branch(output)) } // TODO: make indent take Into, impl Into for vec (branch) capnpc-0.19.0/src/codegen_types.rs000064400000000000000000000416431046102023000151540ustar 00000000000000// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors // Licensed under the MIT License: // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. use crate::codegen::{fmt, GeneratorContext}; use capnp::schema_capnp::{brand, node, type_}; use capnp::Error; use std::collections::hash_map::HashMap; #[derive(Copy, Clone, PartialEq)] pub enum Leaf { Reader(&'static str), Builder(&'static str), Owned, Client, Server, ServerDispatch, Pipeline, GetType, } impl ::std::fmt::Display for Leaf { fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { let display_string = match *self { Self::Reader(lt) => format!("Reader<{lt}>"), Self::Builder(lt) => format!("Builder<{lt}>"), Self::Owned => "Owned".to_string(), Self::Client => "Client".to_string(), Self::Server => "Server".to_string(), Self::ServerDispatch => "ServerDispatch".to_string(), Self::Pipeline => "Pipeline".to_string(), Self::GetType => "get_type".to_string(), }; ::std::fmt::Display::fmt(&display_string, fmt) } } impl Leaf { fn bare_name(&self) -> &'static str { match *self { Self::Reader(_) => "Reader", Self::Builder(_) => "Builder", Self::Owned => "Owned", Self::Client => "Client", Self::Server => "Server", Self::ServerDispatch => "ServerDispatch", Self::GetType => "get_type", Self::Pipeline => "Pipeline", } } fn _have_lifetime(&self) -> bool { match self { &Self::Reader(_) | &Self::Builder(_) => true, &Self::Owned | &Self::Client | &Self::Server | &Self::ServerDispatch | &Self::GetType | &Self::Pipeline => false, } } } pub struct TypeParameterTexts { pub expanded_list: Vec, pub params: String, pub where_clause: String, pub where_clause_with_static: String, pub pipeline_where_clause: String, pub phantom_data_value: String, pub phantom_data_type: String, } // this is a collection of helpers acting on a "Node" (most of them are Type definitions) pub trait RustNodeInfo { fn parameters_texts(&self, ctx: &GeneratorContext) -> TypeParameterTexts; } // this is a collection of helpers acting on a "Type" (someplace where a Type is used, not defined) pub trait RustTypeInfo { fn is_prim(&self) -> Result; fn is_pointer(&self) -> Result; fn is_parameter(&self) -> Result; fn is_branded(&self) -> Result; fn type_string(&self, ctx: &GeneratorContext, module: Leaf) -> Result; } impl<'a> RustNodeInfo for node::Reader<'a> { fn parameters_texts(&self, ctx: &GeneratorContext) -> TypeParameterTexts { if self.get_is_generic() { let params = get_type_parameters(ctx, self.get_id()); let type_parameters = params .iter() .map(|param| param.to_string()) .collect::>() .join(","); let where_clause = "where ".to_string() + &*(params .iter() .map(|param| fmt!(ctx, "{param}: {capnp}::traits::Owned")) .collect::>() .join(", ") + " "); let where_clause_with_static = "where ".to_string() + &*(params .iter() .map(|param| fmt!(ctx, "{param}:'static + {capnp}::traits::Owned")) .collect::>() .join(", ") + " "); let pipeline_where_clause = "where ".to_string() + &*(params.iter().map(|param| { fmt!(ctx, "{param}: {capnp}::traits::Pipelined, <{param} as {capnp}::traits::Pipelined>::Pipeline: {capnp}::capability::FromTypelessPipeline") }).collect::>().join(", ") + " "); let phantom_data_type = if params.len() == 1 { // omit parens to avoid linter error format!("_phantom: ::core::marker::PhantomData<{type_parameters}>") } else { format!("_phantom: ::core::marker::PhantomData<({type_parameters})>") }; let phantom_data_value = "_phantom: ::core::marker::PhantomData,".to_string(); TypeParameterTexts { expanded_list: params, params: type_parameters, where_clause, where_clause_with_static, pipeline_where_clause, phantom_data_type, phantom_data_value, } } else { TypeParameterTexts { expanded_list: vec![], params: "".to_string(), where_clause: "".to_string(), where_clause_with_static: "".to_string(), pipeline_where_clause: "".to_string(), phantom_data_type: "".to_string(), phantom_data_value: "".to_string(), } } } } impl<'a> RustTypeInfo for type_::Reader<'a> { fn type_string(&self, ctx: &GeneratorContext, module: Leaf) -> Result { let local_lifetime = match module { Leaf::Reader(lt) => lt, Leaf::Builder(lt) => lt, _ => "", }; let lifetime_comma = if local_lifetime.is_empty() { "".to_string() } else { format!("{local_lifetime},") }; match self.which()? { type_::Void(()) => Ok("()".to_string()), type_::Bool(()) => Ok("bool".to_string()), type_::Int8(()) => Ok("i8".to_string()), type_::Int16(()) => Ok("i16".to_string()), type_::Int32(()) => Ok("i32".to_string()), type_::Int64(()) => Ok("i64".to_string()), type_::Uint8(()) => Ok("u8".to_string()), type_::Uint16(()) => Ok("u16".to_string()), type_::Uint32(()) => Ok("u32".to_string()), type_::Uint64(()) => Ok("u64".to_string()), type_::Float32(()) => Ok("f32".to_string()), type_::Float64(()) => Ok("f64".to_string()), type_::Text(()) => Ok(fmt!(ctx, "{capnp}::text::{module}")), type_::Data(()) => Ok(fmt!(ctx, "{capnp}::data::{module}")), type_::Struct(st) => do_branding( ctx, st.get_type_id(), st.get_brand()?, module, &ctx.get_qualified_module(st.get_type_id()), ), type_::Interface(interface) => do_branding( ctx, interface.get_type_id(), interface.get_brand()?, module, &ctx.get_qualified_module(interface.get_type_id()), ), type_::List(ot1) => { let element_type = ot1.get_element_type()?; match element_type.which()? { type_::Struct(_) => { let inner = element_type.type_string(ctx, Leaf::Owned)?; Ok(fmt!( ctx, "{capnp}::struct_list::{}<{lifetime_comma}{inner}>", module.bare_name() )) } type_::Enum(_) => { let inner = element_type.type_string(ctx, Leaf::Owned)?; Ok(fmt!( ctx, "{capnp}::enum_list::{}<{lifetime_comma}{inner}>", module.bare_name() )) } type_::List(_) => { let inner = element_type.type_string(ctx, Leaf::Owned)?; Ok(fmt!( ctx, "{capnp}::list_list::{}<{lifetime_comma}{inner}>", module.bare_name() )) } type_::Text(()) => Ok(format!("::capnp::text_list::{module}")), type_::Data(()) => Ok(format!("::capnp::data_list::{module}")), type_::Interface(_) => { let inner = element_type.type_string(ctx, Leaf::Client)?; Ok(fmt!( ctx, "{capnp}::capability_list::{}<{lifetime_comma}{inner}>", module.bare_name() )) } type_::AnyPointer(_) => { Err(Error::failed("List(AnyPointer) is unsupported".to_string())) } _ => { let inner = element_type.type_string(ctx, Leaf::Owned)?; Ok(fmt!( ctx, "{capnp}::primitive_list::{}<{lifetime_comma}{inner}>", module.bare_name() )) } } } type_::Enum(en) => Ok(ctx.get_qualified_module(en.get_type_id())), type_::AnyPointer(pointer) => match pointer.which()? { type_::any_pointer::Parameter(def) => { let the_struct = &ctx.node_map[&def.get_scope_id()]; let parameters = the_struct.get_parameters()?; let parameter = parameters.get(u32::from(def.get_parameter_index())); let parameter_name = parameter.get_name()?.to_str()?; match module { Leaf::Owned => Ok(parameter_name.to_string()), Leaf::Reader(lifetime) => Ok(fmt!( ctx, "<{parameter_name} as {capnp}::traits::Owned>::Reader<{lifetime}>" )), Leaf::Builder(lifetime) => Ok(fmt!( ctx, "<{parameter_name} as {capnp}::traits::Owned>::Builder<{lifetime}>" )), Leaf::Pipeline => Ok(fmt!( ctx, "<{parameter_name} as {capnp}::traits::Pipelined>::Pipeline" )), _ => Err(Error::unimplemented( "unimplemented any_pointer leaf".to_string(), )), } } _ => match module { Leaf::Reader(lifetime) => { Ok(fmt!(ctx, "{capnp}::any_pointer::Reader<{lifetime}>")) } Leaf::Builder(lifetime) => { Ok(fmt!(ctx, "{capnp}::any_pointer::Builder<{lifetime}>")) } _ => Ok(fmt!(ctx, "{capnp}::any_pointer::{module}")), }, }, } } fn is_parameter(&self) -> Result { match self.which()? { type_::AnyPointer(pointer) => match pointer.which()? { type_::any_pointer::Parameter(_) => Ok(true), _ => Ok(false), }, _ => Ok(false), } } fn is_branded(&self) -> Result { match self.which()? { type_::Struct(st) => { let brand = st.get_brand()?; let scopes = brand.get_scopes()?; Ok(!scopes.is_empty()) } _ => Ok(false), } } #[inline(always)] fn is_prim(&self) -> Result { match self.which()? { type_::Int8(()) | type_::Int16(()) | type_::Int32(()) | type_::Int64(()) | type_::Uint8(()) | type_::Uint16(()) | type_::Uint32(()) | type_::Uint64(()) | type_::Float32(()) | type_::Float64(()) | type_::Void(()) | type_::Bool(()) => Ok(true), _ => Ok(false), } } #[inline(always)] fn is_pointer(&self) -> Result { Ok(matches!( self.which()?, type_::Text(()) | type_::Data(()) | type_::List(_) | type_::Struct(_) | type_::Interface(_) | type_::AnyPointer(_) )) } } /// /// pub fn do_branding( ctx: &GeneratorContext, node_id: u64, brand: brand::Reader, leaf: Leaf, the_mod: &str, ) -> Result { let scopes = brand.get_scopes()?; let mut brand_scopes = HashMap::new(); for scope in scopes { brand_scopes.insert(scope.get_scope_id(), scope); } let brand_scopes = brand_scopes; // freeze let mut current_node_id = node_id; let mut accumulator: Vec> = Vec::new(); loop { let current_node = ctx.node_map[¤t_node_id]; let params = current_node.get_parameters()?; let mut arguments: Vec = Vec::new(); match brand_scopes.get(¤t_node_id) { None => { for _ in params { arguments.push(fmt!(ctx, "{capnp}::any_pointer::Owned")); } } Some(scope) => match scope.which()? { brand::scope::Inherit(()) => { for param in params { arguments.push(param.get_name()?.to_string()?); } } brand::scope::Bind(bindings_list_opt) => { let bindings_list = bindings_list_opt?; assert_eq!(bindings_list.len(), params.len()); for binding in bindings_list { match binding.which()? { brand::binding::Unbound(()) => { arguments.push(fmt!(ctx, "{capnp}::any_pointer::Owned")); } brand::binding::Type(t) => { arguments.push(t?.type_string(ctx, Leaf::Owned)?); } } } } }, } accumulator.push(arguments); current_node_id = match ctx.node_parents.get(¤t_node_id).copied() { Some(0) | None => break, Some(id) => id, }; } // Now add a lifetime parameter if the leaf has one. match leaf { Leaf::Reader(lt) => accumulator.push(vec![lt.to_string()]), Leaf::Builder(lt) => accumulator.push(vec![lt.to_string()]), Leaf::ServerDispatch => accumulator.push(vec!["_T".to_string()]), // HACK _ => (), } accumulator.reverse(); let accumulated = accumulator.concat(); let arguments = if !accumulated.is_empty() { format!("<{}>", accumulated.join(",")) } else { "".to_string() }; let maybe_colons = if leaf == Leaf::ServerDispatch || leaf == Leaf::GetType { "::" } else { "" }; // HACK Ok(format!( "{the_mod}::{leaf}{maybe_colons}{arguments}", leaf = leaf.bare_name() )) } pub fn get_type_parameters(ctx: &GeneratorContext, node_id: u64) -> Vec { let mut current_node_id = node_id; let mut accumulator: Vec> = Vec::new(); loop { let current_node = ctx.node_map[¤t_node_id]; let mut params = Vec::new(); for param in current_node.get_parameters().unwrap() { params.push(param.get_name().unwrap().to_string().unwrap()); } accumulator.push(params); current_node_id = match ctx.node_parents.get(¤t_node_id).copied() { Some(0) | None => break, Some(id) => id, }; } accumulator.reverse(); accumulator.concat() } capnpc-0.19.0/src/lib.rs000064400000000000000000000334661046102023000130760ustar 00000000000000// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors // Licensed under the MIT License: // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //! # Cap'n Proto Schema Compiler Plugin Library //! //! This library allows you to do //! [Cap'n Proto code generation](https://capnproto.org/otherlang.html#how-to-write-compiler-plugins) //! within a Cargo build. You still need the `capnp` binary (implemented in C++). //! (If you use a package manager, try looking for a package called //! `capnproto`.) //! //! In your Cargo.toml: //! //! ```ignore //! [dependencies] //! capnp = "0.19" # Note this is a different library than capnp*c* //! //! [build-dependencies] //! capnpc = "0.19" //! ``` //! //! In your build.rs: //! //! ```ignore //! fn main() { //! capnpc::CompilerCommand::new() //! .src_prefix("schema") //! .file("schema/foo.capnp") //! .file("schema/bar.capnp") //! .run().expect("schema compiler command"); //! } //! ``` //! //! In your lib.rs: //! //! ```ignore //! mod foo_capnp { //! include!(concat!(env!("OUT_DIR"), "/foo_capnp.rs")); //! } //! //! mod bar_capnp { //! include!(concat!(env!("OUT_DIR"), "/bar_capnp.rs")); //! } //! ``` //! //! This will be equivalent to executing the shell command //! //! ```ignore //! capnp compile -orust:$OUT_DIR --src-prefix=schema schema/foo.capnp schema/bar.capnp //! ``` pub mod codegen; pub mod codegen_types; mod pointer_constants; use std::{ collections::HashMap, path::{Path, PathBuf}, }; // Copied from capnp/src/lib.rs, where this conversion lives behind the "std" feature flag, // which we don't want to depend on here. pub(crate) fn convert_io_err(err: std::io::Error) -> capnp::Error { use std::io; let kind = match err.kind() { io::ErrorKind::TimedOut => capnp::ErrorKind::Overloaded, io::ErrorKind::BrokenPipe | io::ErrorKind::ConnectionRefused | io::ErrorKind::ConnectionReset | io::ErrorKind::ConnectionAborted | io::ErrorKind::NotConnected => capnp::ErrorKind::Disconnected, _ => capnp::ErrorKind::Failed, }; capnp::Error { extra: format!("{err}"), kind, } } fn run_command( mut command: ::std::process::Command, mut code_generation_command: codegen::CodeGenerationCommand, ) -> ::capnp::Result<()> { let mut p = command.spawn().map_err(convert_io_err)?; code_generation_command.run(p.stdout.take().unwrap())?; let exit_status = p.wait().map_err(convert_io_err)?; if !exit_status.success() { Err(::capnp::Error::failed(format!( "Non-success exit status: {exit_status}" ))) } else { Ok(()) } } /// A builder object for schema compiler commands. #[derive(Default)] pub struct CompilerCommand { files: Vec, src_prefixes: Vec, import_paths: Vec, no_standard_import: bool, executable_path: Option, output_path: Option, default_parent_module: Vec, raw_code_generator_request_path: Option, crate_provides_map: HashMap, } impl CompilerCommand { /// Creates a new, empty command. pub fn new() -> Self { Self::default() } /// Adds a file to be compiled. pub fn file

(&mut self, path: P) -> &mut Self where P: AsRef, { self.files.push(path.as_ref().to_path_buf()); self } /// Adds a --src-prefix flag. For all files specified for compilation that start /// with `prefix`, removes the prefix when computing output filenames. pub fn src_prefix

(&mut self, prefix: P) -> &mut Self where P: AsRef, { self.src_prefixes.push(prefix.as_ref().to_path_buf()); self } /// Adds an --import_path flag. Adds `dir` to the list of directories searched /// for absolute imports. pub fn import_path

(&mut self, dir: P) -> &mut Self where P: AsRef, { self.import_paths.push(dir.as_ref().to_path_buf()); self } /// Specify that `crate_name` provides generated code for `files`. /// /// This means that when your schema refers to types defined in `files` we /// will generate Rust code that uses identifiers in `crate_name`. /// /// # Arguments /// /// - `crate_name`: The Rust identifier of the crate /// - `files`: the Capnp file ids the crate provides generated code for /// /// # When to use /// /// You only need this when your generated code needs to refer to types in /// the external crate. If you just want to use an annotation and the /// argument to that annotation is a builtin type (e.g. `$Json.name`) this /// isn't necessary. /// /// # Example /// /// If you write a schema like so /// /// ```capnp /// // my_schema.capnp /// /// using Json = import "/capnp/compat/json.capnp"; /// /// struct Foo { /// value @0 :Json.Value; /// } /// ``` /// /// you'd look at [json.capnp][json.capnp] to see its capnp id. /// /// ```capnp /// // json.capnp /// /// # Copyright (c) 2015 Sandstorm Development Group, Inc. and contributors ... /// @0x8ef99297a43a5e34; /// ``` /// /// If you want the `foo::Builder::get_value` method generated for your /// schema to return a `capnp_json::json_capnp::value::Reader` you'd add a /// dependency on `capnp_json` to your `Cargo.toml` and specify it provides /// `json.capnp` in your `build.rs`. /// /// ```rust,no_run /// // build.rs /// /// capnpc::CompilerCommand::new() /// .crate_provides("json_capnp", [0x8ef99297a43a5e34]) /// .file("my_schema.capnp") /// .run() /// .unwrap(); /// ``` /// /// [json.capnp]: /// https://github.com/capnproto/capnproto/blob/master/c%2B%2B/src/capnp/compat/json.capnp pub fn crate_provides( &mut self, crate_name: impl Into, files: impl IntoIterator, ) -> &mut Self { let crate_name = crate_name.into(); for file in files.into_iter() { self.crate_provides_map.insert(file, crate_name.clone()); } self } /// Adds the --no-standard-import flag, indicating that the default import paths of /// /usr/include and /usr/local/include should not bet included. pub fn no_standard_import(&mut self) -> &mut Self { self.no_standard_import = true; self } /// Sets the output directory of generated code. Default is OUT_DIR pub fn output_path

(&mut self, path: P) -> &mut Self where P: AsRef, { self.output_path = Some(path.as_ref().to_path_buf()); self } /// Specify the executable which is used for the 'capnp' tool. When this method is not called, the command looks for a name 'capnp' /// on the system (e.g. in working directory or in PATH environment variable). pub fn capnp_executable

(&mut self, path: P) -> &mut Self where P: AsRef, { self.executable_path = Some(path.as_ref().to_path_buf()); self } /// Internal function for starting to build a capnp command. fn new_command(&self) -> ::std::process::Command { if let Some(executable) = &self.executable_path { ::std::process::Command::new(executable) } else { ::std::process::Command::new("capnp") } } /// Sets the default parent module. This indicates the scope in your crate where you will /// add a module containing the generated code. For example, if you set this option to /// `vec!["foo".into(), "bar".into()]`, and you are generating code for `baz.capnp`, then your crate /// should have this structure: /// /// ```ignore /// pub mod foo { /// pub mod bar { /// pub mod baz_capnp { /// include!(concat!(env!("OUT_DIR"), "/baz_capnp.rs")); /// } /// } /// } /// ``` /// /// This option can be overridden by the `parentModule` annotation defined in `rust.capnp`. /// /// If this option is unset, the default is the crate root. pub fn default_parent_module(&mut self, default_parent_module: Vec) -> &mut Self { self.default_parent_module = default_parent_module; self } /// If set, the generator will also write a file containing the raw code generator request to the /// specified path. pub fn raw_code_generator_request_path

(&mut self, path: P) -> &mut Self where P: AsRef, { self.raw_code_generator_request_path = Some(path.as_ref().to_path_buf()); self } /// Runs the command. /// Returns an error if `OUT_DIR` or a custom output directory was not set, or if `capnp compile` fails. pub fn run(&mut self) -> ::capnp::Result<()> { match self.new_command().arg("--version").output() { Err(error) => { return Err(::capnp::Error::failed(format!( "Failed to execute `capnp --version`: {error}. \ Please verify that version 0.5.2 or higher of the capnp executable \ is installed on your system. See https://capnproto.org/install.html" ))) } Ok(output) => { if !output.status.success() { return Err(::capnp::Error::failed(format!( "`capnp --version` returned an error: {:?}. \ Please verify that version 0.5.2 or higher of the capnp executable \ is installed on your system. See https://capnproto.org/install.html", output.status ))); } // TODO Parse the version string? } } let mut command = self.new_command(); // We remove PWD from the env to avoid the following warning. // kj/filesystem-disk-unix.c++:1690: // warning: PWD environment variable doesn't match current directory command.env_remove("PWD"); command.arg("compile").arg("-o").arg("-"); if self.no_standard_import { command.arg("--no-standard-import"); } for import_path in &self.import_paths { command.arg(&format!("--import-path={}", import_path.display())); } for src_prefix in &self.src_prefixes { command.arg(&format!("--src-prefix={}", src_prefix.display())); } for file in &self.files { std::fs::metadata(file).map_err(|error| { let current_dir = match std::env::current_dir() { Ok(current_dir) => format!("`{}`", current_dir.display()), Err(..) => "".to_string(), }; ::capnp::Error::failed(format!( "Unable to stat capnp input file `{}` in working directory {}: {}. \ Please check that the file exists and is accessible for read.", file.display(), current_dir, error )) })?; command.arg(file); } let output_path = if let Some(output_path) = &self.output_path { output_path.clone() } else { // Try `OUT_DIR` by default PathBuf::from(::std::env::var("OUT_DIR").map_err(|error| { ::capnp::Error::failed(format!( "Could not access `OUT_DIR` environment variable: {error}. \ You might need to set it up or instead create you own output \ structure using `CompilerCommand::output_path`" )) })?) }; command.stdout(::std::process::Stdio::piped()); command.stderr(::std::process::Stdio::inherit()); let mut code_generation_command = crate::codegen::CodeGenerationCommand::new(); code_generation_command .output_directory(output_path) .default_parent_module(self.default_parent_module.clone()) .crates_provide_map(self.crate_provides_map.clone()); if let Some(raw_code_generator_request_path) = &self.raw_code_generator_request_path { code_generation_command .raw_code_generator_request_path(raw_code_generator_request_path.clone()); } run_command(command, code_generation_command).map_err(|error| { ::capnp::Error::failed(format!( "Error while trying to execute `capnp compile`: {error}." )) }) } } #[test] #[cfg_attr(miri, ignore)] fn compiler_command_new_no_out_dir() { std::env::remove_var("OUT_DIR"); let error = CompilerCommand::new().run().unwrap_err().extra; assert!(error.starts_with("Could not access `OUT_DIR` environment variable")); } capnpc-0.19.0/src/pointer_constants.rs000064400000000000000000000103771046102023000161000ustar 00000000000000// Copyright (c) 2017 Sandstorm Development Group, Inc. and contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. use capnp::{any_pointer, message}; use crate::codegen::FormattedText::{Branch, Indent, Line}; use crate::codegen::{fmt, indent, line, FormattedText, GeneratorContext}; use crate::codegen_types::{Leaf, RustTypeInfo}; use capnp::schema_capnp::type_; #[derive(Clone, Copy)] pub struct WordArrayDeclarationOptions { pub public: bool, } fn word_array_declaration_aux>( ctx: &GeneratorContext, name: &str, value: T, total_size: ::capnp::MessageSize, options: WordArrayDeclarationOptions, ) -> ::capnp::Result { let allocator = message::HeapAllocator::new().first_segment_words(total_size.word_count as u32 + 1); let mut message = message::Builder::new(allocator); message.set_root(value)?; let words = message.get_segments_for_output()[0]; let mut words_lines = Vec::new(); for index in 0..(words.len() / 8) { let bytes = &words[(index * 8)..(index + 1) * 8]; words_lines.push(Line(fmt!( ctx, "{capnp}::word({}, {}, {}, {}, {}, {}, {}, {}),", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7] ))); } // `static` instead of `const` because these arrays can be large // and consts get inlined at each usage. let vis = if options.public { "pub " } else { "" }; Ok(Branch(vec![ Line(fmt!( ctx, "{}static {}: [{capnp}::Word; {}] = [", vis, name, words.len() / 8 )), indent(Branch(words_lines)), line("];"), ])) } pub fn word_array_declaration( ctx: &GeneratorContext, name: &str, value: any_pointer::Reader, options: WordArrayDeclarationOptions, ) -> ::capnp::Result { word_array_declaration_aux(ctx, name, value, value.target_size()?, options) } pub fn node_word_array_declaration( ctx: &GeneratorContext, name: &str, value: capnp::schema_capnp::node::Reader, options: WordArrayDeclarationOptions, ) -> ::capnp::Result { word_array_declaration_aux(ctx, name, value, value.total_size()?, options) } pub fn generate_pointer_constant( ctx: &GeneratorContext, styled_name: &str, typ: type_::Reader, value: any_pointer::Reader, ) -> ::capnp::Result { Ok(Branch(vec![ Line(fmt!( ctx, "pub static {}: {capnp}::constant::Reader<{}> = {{", styled_name, typ.type_string(ctx, Leaf::Owned)? )), Indent(Box::new(Branch(vec![ word_array_declaration( ctx, "WORDS", value, WordArrayDeclarationOptions { public: false }, )?, Line(fmt!(ctx, "{capnp}::constant::Reader {{")), Indent(Box::new(Branch(vec![ Line("phantom: ::core::marker::PhantomData,".into()), Line("words: &WORDS,".into()), ]))), Line("}".into()), ]))), line("};"), ])) }