[−][src]Crate minicbor_derive
Procedural macros to derive minicbor's Encode
and Decode
traits.
Deriving is supported for struct
s and enum
s. The encoding is optimised
for forward and backward compatibility and the overall approach is
influenced by Google's Protocol Buffers.
The goal is that ideally a change to a type still allows older software, which is unaware of the changes, to decode values of the changed type (forward compatibility) and newer software, which knows about the changes, to decode values of types which have been encoded by older software and which therefore do not include the changes made to the type (backward compatibility).
In order to reach this goal the encoding has the following characteristics:
-
The encoding does not contain any names, i.e. no field names, type names or variant names. Instead every field and every constructor needs to be annotated with an (unsigned) index number, e.g.
#[n(1)]
. -
Unknown fields are ignored during decoding.
-
Optional types default to
None
if their value is not present during decoding. -
Optional enums default to
None
if an unknown variant is encountered during decoding.
Item 1. ensures that names can be changed freely without compatibility concerns. Item 2. ensures that new fields do not affect older software. Item 3. ensures that newer software can stop producing optional values. Item 4. ensures that enums can get new variants that older software is not aware of. By "fields" we mean the elements of structs and tuple structs as well as enum structs and enum tuples. In addition it is a compatible change to turn a unit variant into a struct or tuple variant if all fields are optional.
From the above it should be obvious that non-optional fields need to be present forever, so they should only be part of a type after careful consideration.
It should be emphasised that an enum
itself can not be changed in a
compatible way. An unknown variant causes an error. It is only when they
are declared as an optional field type that unknown variants of an enum
are mapped to None
. In other words, only structs can be used as
top-level types in a forward and backward compatible way, enums can not.
Example
use minicbor::{Encode, Decode}; #[derive(Encode, Decode)] struct Point { #[n(0)] x: f64, #[n(1)] y: f64 } #[derive(Encode, Decode)] struct ConvexHull { #[n(0)] left: Point, #[n(1)] right: Point, #[n(2)] points: Vec<Point>, #[n(3)] state: Option<State> } #[derive(Encode, Decode)] enum State { #[n(0)] Start, #[n(1)] Search { #[n(0)] info: u64 } }
In this example the following changes would be compatible in both directions:
-
Renaming every identifier.
-
Adding optional fields to
Point
,ConvexHull
,State::Start
orState::Search
. -
Adding more variants to
State
iffState
is only decoded as part ofConvexHull
. Direct decoding ofState
would produce anUnknownVariant
error for those new variants.
Attributes and borrowing
Each value needs to be annotated with an index number using either n
or
b
as attribute names. For the encoding it makes no difference which one
to choose. For decoding, b
indicates that the value borrows from the
decoding input, whereas n
produces non-borrowed values (except for
implicit borrows).
Implicit borrowing
By default the following types implicitly borrow from the decoding input, which means their lifetimes are constrained by the input lifetime:
&'_ str
&'_ [u8]
Option<&'_ str>
Option<&'_ [u8]>
Explicit borrowing
If a type is annotated with #[b(...)]
, all its lifetimes will be bound to
the input lifetime. If the type is a std::borrow::Cow
type the generated
code will decode the inner type and construct a Cow::Borrowed
variant
contrary to the Cow
impl of Decode
which produces owned values.
CBOR encoding
The CBOR values which are produced by a derived Encode
implementation are
of the following format.
Structs
Each struct is encoded as a CBOR map with numeric keys:
<<struct encoding>> =
| `map(0)` ; unit struct => empty map
| `begin_map` ; indefinite map otherwise
`0` item_0
`1` item_1
...
`n` item_n
`end`
Optional fields whose value is None
are not encoded.
Enums
Each enum variant is encoded as a two-element array. The first element denotes the variant index and the second the actual variant value:
<<enum encoding>> = `array(2)` n <<struct encoding>>
Derive Macros
Decode | Derive the |
Encode | Derive the |