Description
This is a follow-up to #246. I was thinking about ways to introduce access to the schema metadata for user code, here are my thoughts. I could try to implement a PR for something mentioned below, but it sounds like a lot of work, not sure if I'll handle that...
The problem
Rust has no reflection, also lots of schema information is not introduced in generated code at all. This information could be used for automatic generation of additional traits or altering runtime behavior. In addition, there is a whole annotation system in Cap'n Proto that seems to be completely ignored currently.
#247 introduced a way to access the whole CodeGeneratorRequest
that contains all the information about schema. It resolves the problem but it's pretty hard to use. The goal of this issue is to introduce a usable alternative that will attach relevant information from CodeGeneratorRequest
to relevant places.
Since my personal goal is to do a proc-macro based on the capnp reflection information, I'm going to try and design proc-macro friendly solution. Const functions are resolved on compile time, but there seem to be fundamental obstacles to calling just generated const functions from macro expanding part: rust-lang/rfcs#2279. So the only option to make data available is macro attributes: https://doc.rust-lang.org/reference/attributes.html. In the future there could be a separate macro library that will generate const functions based on annotations so the data could be used at runtime too.
Option 1: generate full metadata
The currently generated code doesn't have actual fields for structs, but each field could be associated with a set of functions in generated Reader
and Builder
structs. I suggest to add the annotation for Reader and Builder structs that will contain a corresponding Node
from schema.capnp
, also add annotation to every single field related function that contains Field
, the Node
from parent struct and the ordinal number of a field in a struct. Also every annotation should contain a map of id -> Node
to all the dependencies and a enum that specifies the annotated object (Reader
struct, Builder
struct, get_*
function, set_*
function, has_*
function, etc...)
Since the annotations are generated, there is no big incentive to make annotations readable. It could be just a single path like #[capnp::meta(<byte string literal>)]
for every single annotation. The byte string is a Cap'n Proto encoded message called MacroMetadata
that contains all the information above. There will be a separate library that will provide a TokenStream
to a MacroMetadata
's Reader
. Also every annotation declaration should generate a function that will take MacroMetadata
and return the Option<Reader>
to the annotation type if that annotation is attached to that object.
Option 2: allow custom annotations that generate only needed metadata
Another solution is based on the ability of Cap'n Proto to annotate annotations. That allows for a less invasive and more elegant solution. We could extend rust.capnp
with generateAttributes
that could be used to annotate user defined annotations that will generate macro attributes and/or derives. Annotation parameters would configure the generated macro: path, format, additional info (field id, type, name, deps, etc...) as well as which structs/functions to annotate. Annotated annotation should generate the code that will help extracting useful data from generated attributes.
With enough flexibility in generateAttributes
parameters that could allow integrating with third-party macros. Correct parameters will generate exactly the macro that a third-party crate expects. On the other hand there could be virtually no parameters so it's similar to Option 1 but just for annotated nodes instead of for everything.