5 releases
0.4.3 | Mar 28, 2021 |
---|---|
0.4.2 | Mar 26, 2021 |
0.3.0 |
|
0.2.0 |
|
0.1.1 | Mar 10, 2021 |
#37 in #big
31KB
497 lines
Serbia
Serde big arrays. An attribute macro to make (de)serializing big arrays painless, roughly following a design proposed by David Tolnay.
Why?
I saw the idea in request-for-implementation. Then I came up with the name.
The name was too good. I had to do it. Don't judge me.
Also: Serbia has some tasty food.
But what is it for?
Serde only implements Serialize
/Deserialize
for arrays of length up to 32. This is due to Rust's current limitation - we can't be generic over array length, so
an arbitrary upper limit was chosen and implementations were generated only up to it.
The crate provides a macro that generates all the code you need to (de)serialize arrays bigger than that.
Status
Under development, but functional. Let me know what's missing or broken!
Usage
Just slap #[serbia]
on top of your type definition. Structs and enums both work!
use serbia::serbia;
#[serbia]
#[derive(Serialize, Deserialize)]
struct S {
arr_big: [u8; 300], // custom serialize/deserialize code generated here
arr_small: [u8; 8], // no custom code - this is handled by Serde fine
}
#[serbia]
#[derive(Serialize, Deserialize)]
enum E {
ArrBig([u8; 300]),
ArrSmall([u8; 22]),
Mixed([u8; 8], [i32; 44], String),
}
If Serbia sees an array length given as a constant, it will generate custom serialize/deserialize code by default, without inspecting whether the constant is larger than 32 or not. This is a limitation of macros.
const BUFSIZE: usize = 22;
#[serbia]
#[derive(Serialize, Deserialize)]
struct S {
arr: [i32; BUFSIZE], // custom serialize/deserialize code generated here
foo: String,
}
Skipping fields
If for some reason you don't want Serbia to generate custom serialize/deserialize code for a field that it would normally handle, you can skip it.
const BUFSIZE: usize = 24;
#[serbia]
#[derive(Serialize, Deserialize)]
struct S {
#[serbia(skip)]
arr_a: [u8; BUFSIZE],
arr_b: [u8; 42],
arr_small: [u8; 8],
}
It's possible to be more granular if needed for some reason.
const BUFSIZE: usize = 24;
#[serbia]
#[derive(Serialize, Deserialize)]
struct S {
#[serbia(skip_serializing, skip_deserializing)]
arr_a: [u8; BUFSIZE],
arr_b: [u8; 42],
arr_small: [u8; 8],
}
Manual array length
You can use the #[serbia(bufsize = ... )]
option to set an array length for
a field. This can be useful to make type aliases work. Constants work here!
type BigArray = [i32; 300];
#[serbia]
#[derive(Serialize, Deserialize)]
struct S {
#[serbia(bufsize = 300)]
arr_a: BigArray,
foo: String,
}
const BUFSIZE: usize = 300;
type BigArray = [i32; BUFSIZE];
#[serbia]
#[derive(Serialize, Deserialize)]
struct S {
#[serbia(bufsize = "BUFSIZE")]
arr_a: BigArray,
foo: String,
}
Interaction with Serde field attributes
Serbia detects when certain Serde field attributes are used and avoids generating code that would cause a conflict, instead yielding to Serde.
#[serbia]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct S {
big_arr: [u8; 40], // serbia generates code for this
#[serde(serialize_with="ser", deserialize_with="de")]
bigger_arr: [u8; 42], // serbia ignores this in favor of the (de)serializers you provided
}
Serbia is intended to play nice with Serde field attributes. If there are problems, please create an issue or submit a PR!
What doesn't work
Nested types.
#[serbia]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct S {
big_arr: Option<[u8; 300]>, // no code generated for this nested array
}
Serbia doesn't yet pick up on Serde variant attributes,
so there might be conflicts there. This can probably be worked around by using
#[serbia(skip)]
on each field that Serbia would try to generate custom
(de)serialization code for.
Dependencies
~1.5MB
~35K SLoC