A minimal binary codec for Rust structs, generated by a derive macro.
cargo add xancodeuse xancode::Codec;
#[derive(Codec)]
struct Greeting {
id: u32,
text: String,
tags: Vec<String>,
}
let msg = Greeting {
id: 1,
text: "hi".into(),
tags: vec!["friendly".into()],
};
let bytes = msg.encode();
let decoded = Greeting::decode(&bytes).unwrap();Every encoded value is length-prefixed:
[ payload_length: u32 BE ][ payload ]
Inside the payload, each field is laid out in declaration order:
| Type | Encoding |
|---|---|
u8..u128, i8..i128 |
big-endian bytes |
f32, f64 |
big-endian bytes (IEEE 754) |
bool |
1 byte (0 = false, 1 = true) |
String |
u32 BE length + UTF-8 bytes |
Vec<T> |
u32 BE element count + each T |
HashSet<T>, BTreeSet<T> |
u32 BE element count + each T |
HashMap<K, V>, BTreeMap<K, V> |
u32 BE entry count + each (K, V) pair |
Option<T> |
1-byte tag (0 = None, 1 = Some) + T if Some |
Nested #[derive(Codec)] struct |
its own length-prefixed encoding |
#[derive(Codec)] enum |
1-byte variant tag + variant fields (see below) |
⚠️ Hash collection determinism
HashMapandHashSetiterate in non-deterministic order, so encoding the same logical value twice can produce different byte sequences. Round-trips and==comparisons still work. If you hash, checksum, dedupe, or diff the wire bytes, useBTreeMap/BTreeSet— they iterate in sorted order and produce stable output.
Codec can be derived on enums. Variants are tagged by their declaration order (0, 1, 2, ...) as a single u8, followed by the variant's fields encoded in order:
#[derive(Codec)]
enum Event {
Tick, // tag 0, no payload
Click { x: i32, y: i32 }, // tag 1, then x, then y
Resize(u32, u32), // tag 2, then both u32s
}All three variant kinds (unit, tuple, struct) are supported. Limit: up to 256 variants per enum.
⚠️ Wire stabilityVariant tags are positional, not based on Rust's discriminant values or variant names. Reordering variants, removing one, or inserting a non-trailing variant breaks compatibility with already-serialized data. Treat the variant list as an append-only schema once it's in the wild.
Path types only: the primitives above, String, Vec<T>, HashSet<T>, BTreeSet<T>, HashMap<K, V>, BTreeMap<K, V>, Option<T>, and any other type implementing Codec (including enums and nested structs).
References, tuples, fixed-size arrays, and generics on the deriving type are not supported. Empty enums and unit / tuple structs cannot derive Codec.
encode always succeeds. decode returns Result<Self, Box<dyn Error>> and errors on truncated input, invalid Option tags, unknown enum tags, invalid bool values, or invalid UTF-8.