Have you ever wanted to, say, define both a human-readable and a compact Serde serializations for the same struct/enum?
You can define multiple structs with the same fields but different serde
attributes on these fields and then transmute between the struct (reference) types to pick a serialization.
But manually writing copies of the same struct is basically unmaintainable and looks bad in the code!
Here's a proc macro to automate that:
https://crates.io/crates/attrsets | GitHub - unrelentingtech/attrsets: Rust proc macro for defining multiple variants of a struct/enum with different attribute annotations e.g. for multiple Serde serializations | now on https://codeberg.org/unrelentingtech/attrsets
#[attrsets::attrsets(Compact)]
#[derive(Deserialize, Serialize)]
pub struct Thing {
#[attrset(Compact, serde(rename = "f"))]
pub flag: bool,
#[attrset(Compact, serde(with = "ts_seconds"))]
#[attrset(Compact, serde(rename = "t"))]
pub time: DateTime<Utc>,
}
basically expands into
#[derive(Deserialize, Serialize)]
pub struct Thing {
pub flag: bool,
pub time: DateTime<Utc>,
}
#[derive(Deserialize, Serialize)]
pub struct ThingCompact {
#[serde(rename = "f")]
pub flag: bool,
#[serde(with = "ts_seconds")]
#[serde(rename = "t")]
pub time: DateTime<Utc>,
}
and now you can
let thing = ThingCompact { flag, time };
// the Compact variant here is intended for a non-readable format like CBOR, but let's view JSON
println!("{}", serde_json::to_string(&thing).unwrap());
// {"f":true,"t":1594561453}
println!("{}", serde_json::to_string(unsafe { mem::transmute::<_, &Thing>(&thing) }).unwrap());
// {"flag":true,"time":"2020-07-12T13:44:13Z"}