-
Notifications
You must be signed in to change notification settings - Fork 235
Description
The Problem
Suppose we have this schema in a file named example.capnp
:
@0xcb795246146676e8;
struct Foo {}
struct Bar {
foo @0 :Foo;
}
In the code that capnpc-rust generates for this schema, accessor methods for the Bar.foo
field refer to the type Foo
using a full absolute module path, like crate::example_capnp::foo::Reader
. This implies that the generated code must be included at the top level of a crate -- something that not everyone wants to do.
The parentModule
annotation provides a way to work around this limitation, but it's cumbersome to use.
Another workaround is the default_parent_module
option, but it too does not meet everyone's needs.
We could avoid a lot of awkwardness if the generated code could somehow just work when dropped in at any module location.
A Questionable Solution
We could use Rust's super
keyword to make everything relative. Then the type in the above example would be super::foo::Reader
, which does not depend on the position of the code in the module structure of the crate.
However, there are two reasons that this approach is not completely satisfactory:
- For schema files with nested structs, we would end up with lots of paths like
super::super::super::baz::Reader
, which seems potentially confusing to humans reading the code. - Tracking the relative paths would require some additional complexity in
codegen.rs
.
A Better Solution
Inspired by this example from recapn, we can use the super
keyword together with the use ... as ...
pattern.
pub mod example_capnp {
use super::example_capnp as _file;
pub mod _imports { ... }
pub mod foo {
use super::_file as _file;
// ...
}
pub mod bar {
use super::_file as _file;
// ... accessors that refer to `_file::foo::Reader`
}
}
The idea is to ensure that in every generated module, the module path _file
is in scope and refers to the top-level generated module of the corresponding schema file.
To address the case when there are multiple schema files, some of which might import others, we add a mod _imports
in every _file
module. This module will resolve any cross-file module references that need to happen; the code generator can easily populate it using the RequestedFile.imports
field.
We should still keep the parentModule
annotation for now, for compatibility purposes, but this new setup should eliminate the need for it in most cases.