capnproto-rust
capnproto-rust copied to clipboard
Reading raw enum value and setting on a builder
Suppose I have a capnproto enum definition. Is there any way to get the raw u16 value of the enum and set the same raw u16 value on a builder as an enum field's value?
The idea is that I am using different versions of the capnproto definitions. The original definition has an enum with a few variants. It also has two struct definitions which each have a field with the enum type. First, I have application A with the original protocol definition. Later, I evolve the protocol and add more variants to an enum definition and use the new definitions in a different application B which sends messages to application A.
Application A could be a proxy or logging application. For instance, it could be an application that is copying an inbound message's enum value, and then setting the enum value on a different message.
I understand there would be some risks with allowing any u16 value to be set.
You can get the raw u16 value via the ToU16 trait, which is implemented by all generated enums: https://github.com/capnproto/capnproto-rust/blob/aa09220f26ed6df5af76cefc804879bdf6aacdcc/capnp/src/traits.rs#L98
There is currently no supported way to set an enum to an arbitrary u16 value. You could try std::mem::transmute(), but I'd be worried about potential undefined behavior.
Looking at generated code, the enum getters are like get_field(self) -> Result<EnumType, NotInSchema>. If the protocol evolved with more enum variants, then I assume that application A (with the original protocol without the new variants) will always get Err(NotInSchema) for any message set with a new variant value.
I was thinking about modifying the code generator to also add methods (only for enum fields) like:
pub fn set_field_raw(&mut self, value: u16);
pub fn get_field_raw(self) -> u16;
Is that feasible or ill-advised? If it's ok, I can also try to submit a PR.
Adding new methods like that makes sense to me. I would slightly adjust the naming:
pub fn set_raw_field(&mut self, value: u16);
pub fn get_raw_field(self) -> u16;
That way, set_raw_ and get_raw_ are just new prefixes like the existing get_, set_ and init_.