From a0c37e85345acf6671cb377e71ef5ecd6638617b Mon Sep 17 00:00:00 2001 From: Daniel Franklin Date: Sun, 11 Jun 2023 22:03:34 -0600 Subject: [PATCH] Add $Rust.imports Relates to #411 and #233 This api is quite awkward but I'm intending it as a stopgap until cargo supports non-linking DEP environment variables. But it allows people to use generated code in external crates without us needing to add an annotation to the external `.capnp` file. This is important because without the change to cargo it would be awkward to require users use a modified `json.capnp`. --- Cargo.toml | 1 + capnp/src/lib.rs | 1 + capnp/src/rust_capnp.rs | 268 ++++++++++++++++++++++ capnpc/rust.capnp | 26 +++ capnpc/src/codegen.rs | 62 ++++- capnpc/test-edition-2015/Cargo.toml | 1 + capnpc/test-edition-2015/test.rs | 1 + capnpc/test-edition-2018/Cargo.toml | 1 + capnpc/test-edition-2021/Cargo.toml | 1 + capnpc/test/Cargo.toml | 1 + capnpc/test/external-crate/Cargo.toml | 12 + capnpc/test/external-crate/build.rs | 7 + capnpc/test/external-crate/external.capnp | 7 + capnpc/test/external-crate/src/lib.rs | 3 + capnpc/test/test.capnp | 13 +- regenerate-rust-annotations.sh | 7 + 16 files changed, 406 insertions(+), 6 deletions(-) create mode 100644 capnp/src/rust_capnp.rs create mode 100644 capnpc/test/external-crate/Cargo.toml create mode 100644 capnpc/test/external-crate/build.rs create mode 100644 capnpc/test/external-crate/external.capnp create mode 100644 capnpc/test/external-crate/src/lib.rs create mode 100755 regenerate-rust-annotations.sh diff --git a/Cargo.toml b/Cargo.toml index 64e9c0dc6..2bfefe565 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ members = [ "async-byte-channel", "benchmark", "capnpc/test", + "capnpc/test/external-crate", "capnpc/test-edition-2015", "capnpc/test-edition-2018", "capnpc/test-edition-2021", diff --git a/capnp/src/lib.rs b/capnp/src/lib.rs index 3ca1360d2..cc2847a8d 100644 --- a/capnp/src/lib.rs +++ b/capnp/src/lib.rs @@ -54,6 +54,7 @@ pub mod message; pub mod primitive_list; pub mod private; pub mod raw; +pub mod rust_capnp; pub mod schema; pub mod serialize; pub mod serialize_packed; diff --git a/capnp/src/rust_capnp.rs b/capnp/src/rust_capnp.rs new file mode 100644 index 000000000..9fa322951 --- /dev/null +++ b/capnp/src/rust_capnp.rs @@ -0,0 +1,268 @@ +// @generated by the capnpc-rust plugin to the Cap'n Proto schema compiler. +// DO NOT EDIT. +// source: rust.capnp + +pub mod name { + pub const ID: u64 = 0xc2fe4c6d100166d0; + pub fn get_type() -> crate::introspect::Type { ::introspect() } +} +pub mod parent_module { + pub const ID: u64 = 0xabee386cd1450364; + pub fn get_type() -> crate::introspect::Type { ::introspect() } +} +pub mod imports { + pub const ID: u64 = 0xc3b9fe42a83105cd; + pub fn get_type() -> crate::introspect::Type { as crate::introspect::Introspect>::introspect() } +} + +pub mod import { + #[derive(Copy, Clone)] + pub struct Owned(()); + impl crate::introspect::Introspect for Owned { fn introspect() -> crate::introspect::Type { crate::introspect::TypeVariant::Struct(crate::introspect::RawBrandedStructSchema { generic: &_private::RAW_SCHEMA, field_types: _private::get_field_types, annotation_types: _private::get_annotation_types }).into() } } + impl crate::traits::Owned for Owned { type Reader<'a> = Reader<'a>; type Builder<'a> = Builder<'a>; } + impl crate::traits::OwnedStruct for Owned { type Reader<'a> = Reader<'a>; type Builder<'a> = Builder<'a>; } + impl crate::traits::Pipelined for Owned { type Pipeline = Pipeline; } + + pub struct Reader<'a> { reader: crate::private::layout::StructReader<'a> } + impl <'a,> ::core::marker::Copy for Reader<'a,> {} + impl <'a,> ::core::clone::Clone for Reader<'a,> { + fn clone(&self) -> Self { *self } + } + + impl <'a,> crate::traits::HasTypeId for Reader<'a,> { + const TYPE_ID: u64 = _private::TYPE_ID; + } + impl <'a,> ::core::convert::From> for Reader<'a,> { + fn from(reader: crate::private::layout::StructReader<'a>) -> Self { + Self { reader, } + } + } + + impl <'a,> ::core::convert::From> for crate::dynamic_value::Reader<'a> { + fn from(reader: Reader<'a,>) -> Self { + Self::Struct(crate::dynamic_struct::Reader::new(reader.reader, crate::schema::StructSchema::new(crate::introspect::RawBrandedStructSchema { generic: &_private::RAW_SCHEMA, field_types: _private::get_field_types::<>, annotation_types: _private::get_annotation_types::<>}))) + } + } + + impl <'a,> ::core::fmt::Debug for Reader<'a,> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::result::Result<(), ::core::fmt::Error> { + core::fmt::Debug::fmt(&::core::convert::Into::>::into(*self), f) + } + } + + impl <'a,> crate::traits::FromPointerReader<'a> for Reader<'a,> { + fn get_from_pointer(reader: &crate::private::layout::PointerReader<'a>, default: ::core::option::Option<&'a [crate::Word]>) -> crate::Result { + ::core::result::Result::Ok(reader.get_struct(default)?.into()) + } + } + + impl <'a,> crate::traits::IntoInternalStructReader<'a> for Reader<'a,> { + fn into_internal_struct_reader(self) -> crate::private::layout::StructReader<'a> { + self.reader + } + } + + impl <'a,> crate::traits::Imbue<'a> for Reader<'a,> { + fn imbue(&mut self, cap_table: &'a crate::private::layout::CapTable) { + self.reader.imbue(crate::private::layout::CapTableReader::Plain(cap_table)) + } + } + + impl <'a,> Reader<'a,> { + pub fn reborrow(&self) -> Reader<'_,> { + Self { .. *self } + } + + pub fn total_size(&self) -> crate::Result { + self.reader.total_size() + } + #[inline] + pub fn get_path(self) -> crate::Result> { + crate::traits::FromPointerReader::get_from_pointer(&self.reader.get_pointer_field(0), ::core::option::Option::None) + } + #[inline] + pub fn has_path(&self) -> bool { + !self.reader.get_pointer_field(0).is_null() + } + #[inline] + pub fn get_crate(self) -> crate::Result> { + crate::traits::FromPointerReader::get_from_pointer(&self.reader.get_pointer_field(1), ::core::option::Option::None) + } + #[inline] + pub fn has_crate(&self) -> bool { + !self.reader.get_pointer_field(1).is_null() + } + } + + pub struct Builder<'a> { builder: crate::private::layout::StructBuilder<'a> } + impl <'a,> crate::traits::HasStructSize for Builder<'a,> { + const STRUCT_SIZE: crate::private::layout::StructSize = crate::private::layout::StructSize { data: 0, pointers: 2 }; + } + impl <'a,> crate::traits::HasTypeId for Builder<'a,> { + const TYPE_ID: u64 = _private::TYPE_ID; + } + impl <'a,> ::core::convert::From> for Builder<'a,> { + fn from(builder: crate::private::layout::StructBuilder<'a>) -> Self { + Self { builder, } + } + } + + impl <'a,> ::core::convert::From> for crate::dynamic_value::Builder<'a> { + fn from(builder: Builder<'a,>) -> Self { + Self::Struct(crate::dynamic_struct::Builder::new(builder.builder, crate::schema::StructSchema::new(crate::introspect::RawBrandedStructSchema { generic: &_private::RAW_SCHEMA, field_types: _private::get_field_types::<>, annotation_types: _private::get_annotation_types::<>}))) + } + } + + impl <'a,> crate::traits::ImbueMut<'a> for Builder<'a,> { + fn imbue_mut(&mut self, cap_table: &'a mut crate::private::layout::CapTable) { + self.builder.imbue(crate::private::layout::CapTableBuilder::Plain(cap_table)) + } + } + + impl <'a,> crate::traits::FromPointerBuilder<'a> for Builder<'a,> { + fn init_pointer(builder: crate::private::layout::PointerBuilder<'a>, _size: u32) -> Self { + builder.init_struct(::STRUCT_SIZE).into() + } + fn get_from_pointer(builder: crate::private::layout::PointerBuilder<'a>, default: ::core::option::Option<&'a [crate::Word]>) -> crate::Result { + ::core::result::Result::Ok(builder.get_struct(::STRUCT_SIZE, default)?.into()) + } + } + + impl <'a,> crate::traits::SetPointerBuilder for Reader<'a,> { + fn set_pointer_builder(mut pointer: crate::private::layout::PointerBuilder<'_>, value: Self, canonicalize: bool) -> crate::Result<()> { pointer.set_struct(&value.reader, canonicalize) } + } + + impl <'a,> Builder<'a,> { + pub fn into_reader(self) -> Reader<'a,> { + self.builder.into_reader().into() + } + pub fn reborrow(&mut self) -> Builder<'_,> { + Builder { builder: self.builder.reborrow() } + } + pub fn reborrow_as_reader(&self) -> Reader<'_,> { + self.builder.as_reader().into() + } + + pub fn total_size(&self) -> crate::Result { + self.builder.as_reader().total_size() + } + #[inline] + pub fn get_path(self) -> crate::Result> { + crate::traits::FromPointerBuilder::get_from_pointer(self.builder.get_pointer_field(0), ::core::option::Option::None) + } + #[inline] + pub fn set_path(&mut self, value: crate::text::Reader<'_>) { + self.builder.reborrow().get_pointer_field(0).set_text(value); + } + #[inline] + pub fn init_path(self, size: u32) -> crate::text::Builder<'a> { + self.builder.get_pointer_field(0).init_text(size) + } + #[inline] + pub fn has_path(&self) -> bool { + !self.builder.is_pointer_field_null(0) + } + #[inline] + pub fn get_crate(self) -> crate::Result> { + crate::traits::FromPointerBuilder::get_from_pointer(self.builder.get_pointer_field(1), ::core::option::Option::None) + } + #[inline] + pub fn set_crate(&mut self, value: crate::text::Reader<'_>) { + self.builder.reborrow().get_pointer_field(1).set_text(value); + } + #[inline] + pub fn init_crate(self, size: u32) -> crate::text::Builder<'a> { + self.builder.get_pointer_field(1).init_text(size) + } + #[inline] + pub fn has_crate(&self) -> bool { + !self.builder.is_pointer_field_null(1) + } + } + + pub struct Pipeline { _typeless: crate::any_pointer::Pipeline } + impl crate::capability::FromTypelessPipeline for Pipeline { + fn new(typeless: crate::any_pointer::Pipeline) -> Self { + Self { _typeless: typeless, } + } + } + impl Pipeline { + } + mod _private { + pub static ENCODED_NODE: [crate::Word; 47] = [ + crate::word(0, 0, 0, 0, 5, 0, 6, 0), + crate::word(98, 59, 209, 191, 187, 136, 72, 196), + crate::word(11, 0, 0, 0, 1, 0, 0, 0), + crate::word(131, 208, 141, 60, 76, 193, 179, 131), + crate::word(2, 0, 7, 0, 0, 0, 0, 0), + crate::word(0, 0, 0, 0, 0, 0, 0, 0), + crate::word(21, 0, 0, 0, 146, 0, 0, 0), + crate::word(29, 0, 0, 0, 7, 0, 0, 0), + crate::word(0, 0, 0, 0, 0, 0, 0, 0), + crate::word(25, 0, 0, 0, 119, 0, 0, 0), + crate::word(0, 0, 0, 0, 0, 0, 0, 0), + crate::word(0, 0, 0, 0, 0, 0, 0, 0), + crate::word(114, 117, 115, 116, 46, 99, 97, 112), + crate::word(110, 112, 58, 73, 109, 112, 111, 114), + crate::word(116, 0, 0, 0, 0, 0, 0, 0), + crate::word(0, 0, 0, 0, 1, 0, 1, 0), + crate::word(8, 0, 0, 0, 3, 0, 4, 0), + crate::word(0, 0, 0, 0, 0, 0, 0, 0), + crate::word(0, 0, 1, 0, 0, 0, 0, 0), + crate::word(0, 0, 0, 0, 0, 0, 0, 0), + crate::word(41, 0, 0, 0, 42, 0, 0, 0), + crate::word(0, 0, 0, 0, 0, 0, 0, 0), + crate::word(36, 0, 0, 0, 3, 0, 1, 0), + crate::word(48, 0, 0, 0, 2, 0, 1, 0), + crate::word(1, 0, 0, 0, 1, 0, 0, 0), + crate::word(0, 0, 1, 0, 1, 0, 0, 0), + crate::word(0, 0, 0, 0, 0, 0, 0, 0), + crate::word(45, 0, 0, 0, 50, 0, 0, 0), + crate::word(0, 0, 0, 0, 0, 0, 0, 0), + crate::word(40, 0, 0, 0, 3, 0, 1, 0), + crate::word(52, 0, 0, 0, 2, 0, 1, 0), + crate::word(112, 97, 116, 104, 0, 0, 0, 0), + crate::word(12, 0, 0, 0, 0, 0, 0, 0), + crate::word(0, 0, 0, 0, 0, 0, 0, 0), + crate::word(0, 0, 0, 0, 0, 0, 0, 0), + crate::word(0, 0, 0, 0, 0, 0, 0, 0), + crate::word(12, 0, 0, 0, 0, 0, 0, 0), + crate::word(0, 0, 0, 0, 0, 0, 0, 0), + crate::word(0, 0, 0, 0, 0, 0, 0, 0), + crate::word(99, 114, 97, 116, 101, 0, 0, 0), + crate::word(12, 0, 0, 0, 0, 0, 0, 0), + crate::word(0, 0, 0, 0, 0, 0, 0, 0), + crate::word(0, 0, 0, 0, 0, 0, 0, 0), + crate::word(0, 0, 0, 0, 0, 0, 0, 0), + crate::word(12, 0, 0, 0, 0, 0, 0, 0), + crate::word(0, 0, 0, 0, 0, 0, 0, 0), + crate::word(0, 0, 0, 0, 0, 0, 0, 0), + ]; + pub fn get_field_types(index: u16) -> crate::introspect::Type { + match index { + 0 => ::introspect(), + 1 => ::introspect(), + _ => panic!("invalid field index {}", index), + } + } + pub fn get_annotation_types(child_index: Option, index: u32) -> crate::introspect::Type { + panic!("invalid annotation indices ({:?}, {}) ", child_index, index) + } + pub static RAW_SCHEMA: crate::introspect::RawStructSchema = crate::introspect::RawStructSchema { + encoded_node: &ENCODED_NODE, + nonunion_members: NONUNION_MEMBERS, + members_by_discriminant: MEMBERS_BY_DISCRIMINANT, + }; + pub static NONUNION_MEMBERS : &[u16] = &[0,1]; + pub static MEMBERS_BY_DISCRIMINANT : &[u16] = &[]; + pub const TYPE_ID: u64 = 0xc448_88bb_bfd1_3b62; + } +} +pub mod crate_ { + pub const ID: u64 = 0xc1763f46d790815c; + pub fn get_type() -> crate::introspect::Type { ::introspect() } +} +pub mod option { + pub const ID: u64 = 0xabfef22c4ee1964e; + pub fn get_type() -> crate::introspect::Type { <() as crate::introspect::Introspect>::introspect() } +} diff --git a/capnpc/rust.capnp b/capnpc/rust.capnp index 4636baa3a..f95e8ffc5 100644 --- a/capnpc/rust.capnp +++ b/capnpc/rust.capnp @@ -27,6 +27,32 @@ annotation parentModule @0xabee386cd1450364 (file) :Text; # } # } +annotation imports @0xc3b9fe42a83105cd (file) :List(Import); +# Allows specifying that the generated code for an import is located in another +# crate. +# +# You only need this if your code uses imported types whose generated code is +# in another crate. You can only use this annotation once in all the files +# compiled together. +# +# using Json = import "/capnp/compat/json.capnp"; +# +# $Rust.imports([ +# (path = "/capnp/compat/json.capnp", crate = "capnp_json") +# ]); + +struct Import { + path @0 :Text; + crate @1 :Text; +} + +annotation crate @0xc1763f46d790815c (file) :Text; +# The Rust crate that provides the generated code. +# +# You need this if you're providing a library to be used by other crates. If +# you're only using the generated code in your own crate then you don't need to +# change from the default. + annotation option @0xabfef22c4ee1964e (field) :Void; # Make the generated getters return Option instead of T. Supported on # pointer types (e.g. structs, lists, and blobs). diff --git a/capnpc/src/codegen.rs b/capnpc/src/codegen.rs index 2db5dfdb0..9eb623621 100644 --- a/capnpc/src/codegen.rs +++ b/capnpc/src/codegen.rs @@ -23,9 +23,9 @@ use std::collections; use std::collections::{HashMap, HashSet}; use std::path::{Path, PathBuf}; -use capnp; -use capnp::schema_capnp; use capnp::Error; +use capnp::{self, rust_capnp}; +use capnp::{schema_capnp, struct_list}; use self::FormattedText::{BlankLine, Branch, Indent, Line}; use crate::codegen_types::{do_branding, Leaf, RustNodeInfo, RustTypeInfo, TypeParameterTexts}; @@ -231,17 +231,46 @@ impl<'a> GeneratorContext<'a> { } } + let mut rust_imports = None; + for requested_file in ctx.request.get_requested_files()? { + let node = ctx + .node_map + .get(&requested_file.get_id()) + .ok_or(Error::failed("file node not found".into()))? + .reborrow(); + let fname = requested_file.get_filename()?; + + if let Some(file_imports) = get_rust_imports(node)? { + if let Some((prev_fname, _)) = rust_imports { + return Err(Error::failed(format!( + "Conflicting Rust.imports annotations in {prev_fname} and {fname}. \ + You can only use the annotation once across all the files compiled together" + ))); + } else { + rust_imports = Some((fname.to_string(), file_imports)); + } + } + } + let rust_imports = rust_imports.map(|(_fname, map)| map).unwrap_or_default(); + 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()?); + let raw_path = import.get_name()?; + let importpath = ::std::path::Path::new(raw_path); let root_name: String = format!( "{}_capnp", path_to_stem_string(importpath)?.replace('-', "_") ); + let parent_module_scope = if let Some(krate) = rust_imports.get(raw_path) { + vec![format!("::{krate}")] + } else { + default_parent_module_scope.clone() + }; + ctx.populate_scope_map( - default_parent_module_scope.clone(), + parent_module_scope, root_name, NameKind::Verbatim, import.get_id(), @@ -284,7 +313,9 @@ impl<'a> GeneratorContext<'a> { 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 { - ancestor_scope_names = vec!["crate".to_string()]; + 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)?); } } @@ -352,6 +383,27 @@ macro_rules! fmt( pub(crate) use fmt; +fn get_rust_imports( + file: schema_capnp::node::Reader, +) -> capnp::Result>> { + for annot in file.get_annotations()? { + if annot.get_id() == rust_capnp::imports::ID { + let schema_capnp::value::Which::List(ptr) = annot.get_value()?.which()? else { + return Err(Error::failed("Rust.imports not a list".into())); + }; + let list = ptr.get_as::>()?; + let mut map = HashMap::new(); + for import in list { + let path = import.get_path()?; + let krate = import.get_crate()?; + map.insert(path.to_string(), krate.to_string()); + } + return Ok(Some(map)); + } + } + Ok(None) +} + fn path_to_stem_string>(path: P) -> ::capnp::Result { match path.as_ref().file_stem() { None => Err(Error::failed(format!( diff --git a/capnpc/test-edition-2015/Cargo.toml b/capnpc/test-edition-2015/Cargo.toml index 8007b5163..ab3946511 100644 --- a/capnpc/test-edition-2015/Cargo.toml +++ b/capnpc/test-edition-2015/Cargo.toml @@ -16,3 +16,4 @@ capnpc = { path = "../" } [dependencies] capnp = { path = "../../capnp" } capnpc = { path = "../" } +external-crate = { path = "../test/external-crate" } diff --git a/capnpc/test-edition-2015/test.rs b/capnpc/test-edition-2015/test.rs index 367209a33..ac062c2f3 100644 --- a/capnpc/test-edition-2015/test.rs +++ b/capnpc/test-edition-2015/test.rs @@ -1,5 +1,6 @@ extern crate capnp; extern crate core; +extern crate external_crate; pub mod test_capnp { include!(concat!(env!("OUT_DIR"), "/test_capnp.rs")); diff --git a/capnpc/test-edition-2018/Cargo.toml b/capnpc/test-edition-2018/Cargo.toml index 652c065f4..eefe87fcf 100644 --- a/capnpc/test-edition-2018/Cargo.toml +++ b/capnpc/test-edition-2018/Cargo.toml @@ -16,3 +16,4 @@ capnpc = { path = "../" } [dependencies] capnp = { path = "../../capnp" } capnpc = { path = "../" } +external-crate = { path = "../test/external-crate" } diff --git a/capnpc/test-edition-2021/Cargo.toml b/capnpc/test-edition-2021/Cargo.toml index 1f2034561..64d792af0 100644 --- a/capnpc/test-edition-2021/Cargo.toml +++ b/capnpc/test-edition-2021/Cargo.toml @@ -16,3 +16,4 @@ capnpc = { path = "../" } [dependencies] capnp = { path = "../../capnp" } capnpc = { path = "../" } +external-crate = { path = "../test/external-crate" } diff --git a/capnpc/test/Cargo.toml b/capnpc/test/Cargo.toml index bb7565d69..c6cc5d6a0 100644 --- a/capnpc/test/Cargo.toml +++ b/capnpc/test/Cargo.toml @@ -17,3 +17,4 @@ capnpc = { path = "../" } [dependencies] capnp = { path = "../../capnp" } capnpc = { path = "../" } +external-crate = { path = "./external-crate" } diff --git a/capnpc/test/external-crate/Cargo.toml b/capnpc/test/external-crate/Cargo.toml new file mode 100644 index 000000000..f966cf1a5 --- /dev/null +++ b/capnpc/test/external-crate/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "external-crate" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +capnp = { version = "0.17.1", path = "../../../capnp" } + +[build-dependencies] +capnpc = { version = "0.17.1", path = "../.." } diff --git a/capnpc/test/external-crate/build.rs b/capnpc/test/external-crate/build.rs new file mode 100644 index 000000000..37049b524 --- /dev/null +++ b/capnpc/test/external-crate/build.rs @@ -0,0 +1,7 @@ +fn main() { + capnpc::CompilerCommand::new() + .file("external.capnp") + .import_path("../") + .run() + .expect("compiling schema"); +} diff --git a/capnpc/test/external-crate/external.capnp b/capnpc/test/external-crate/external.capnp new file mode 100644 index 000000000..bbe72587d --- /dev/null +++ b/capnpc/test/external-crate/external.capnp @@ -0,0 +1,7 @@ +@0xe6f94f52f7be8fe2; + +annotation annot (*) :Opts; + +struct Opts { + field @0 :Text; +} diff --git a/capnpc/test/external-crate/src/lib.rs b/capnpc/test/external-crate/src/lib.rs new file mode 100644 index 000000000..6f48749cb --- /dev/null +++ b/capnpc/test/external-crate/src/lib.rs @@ -0,0 +1,3 @@ +pub mod external_capnp { + include!(concat!(env!("OUT_DIR"), "/external_capnp.rs")); +} diff --git a/capnpc/test/test.capnp b/capnpc/test/test.capnp index 319e14373..fe9c596bc 100644 --- a/capnpc/test/test.capnp +++ b/capnpc/test/test.capnp @@ -23,8 +23,19 @@ @0x99d187209d25cee7; using Rust = import "rust.capnp"; +using External = import "./external-crate/external.capnp"; -struct FieldSubsetIndexesCorrectly { +$Rust.imports([ + (path = "./external-crate/external.capnp", crate = "external_crate") +]); + +# The test case is that this builds. This ensure we're able to refer to a struct +# (external_capnp::opts) in the generated code. +struct UseExternalAnnotation $External.annot(field = "foo") { + field @0 :Text; +} + +struct FieldSubsetIndexesCorrectly{ common @2 :Text; union { diff --git a/regenerate-rust-annotations.sh b/regenerate-rust-annotations.sh new file mode 100755 index 000000000..550ec32e3 --- /dev/null +++ b/regenerate-rust-annotations.sh @@ -0,0 +1,7 @@ +#! /bin/sh + +set -e +set -x + +cargo build -p capnpc +capnp compile -otarget/debug/capnpc-rust-bootstrap:capnp/src capnpc/rust.capnp --src-prefix capnpc/