Skip to content

Instantly share code, notes, and snippets.

@Akanoa
Created November 7, 2024 06:36
Show Gist options
  • Save Akanoa/1e040648c01ea510a5ebb048c4a26cef to your computer and use it in GitHub Desktop.
Save Akanoa/1e040648c01ea510a5ebb048c4a26cef to your computer and use it in GitHub Desktop.
macro_rules! versioned_schema {
($schema:tt, $($version:tt => $structure:ident,)+) => {
use serde::de::{self, Deserialize, Deserializer, Visitor, SeqAccess};
use serde::ser::{Serialize, Serializer, SerializeTuple};
use std::fmt;
#[derive(Debug, PartialEq)]
enum $schema {
$(
$structure($structure),
)+
}
impl Serialize for $schema {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer
{
let mut tuple = serializer.serialize_tuple(2)?;
match *self {
$(
$schema::$structure(ref v) => {
// serialize the version as one byte
tuple.serialize_element(&($version as u8))?;
// serialize the content
tuple.serialize_element(v)?;
}
)+
}
tuple.end()
}
}
impl<'de> Deserialize<'de> for $schema
where $($structure: Deserialize<'de>),+
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de>
{
struct MyVisitor;
impl<'de> Visitor<'de> for MyVisitor {
type Value = $schema;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("versioned schema")
}
fn visit_seq<S>(self, mut seq: S) -> Result<Self::Value, S::Error>
where S: SeqAccess<'de>
{
// deserialize the version as one byte
match seq.next_element::<u8>()?
.ok_or_else(|| {
de::Error::custom("missing schema version")
})?
{
$(
$version => {
// deserialize the content
seq.next_element()?
.ok_or_else(|| de::Error::custom(
concat!("missing data for version ", $version)
))
.map($schema::$structure)
}
)+
other => {
Err(de::Error::custom(format_args!("unknown version: {}", other)))
}
}
}
}
deserializer.deserialize_tuple(2, MyVisitor)
}
}
};
}
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct A {
field1: u8,
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct AV2 {
field1: u8,
#[serde(default)]
field2: Option<usize>,
}
#[test]
fn toto() {
versioned_schema! {
SchemaA,
0 => A,
1 => AV2,
}
let a = SchemaA::A(A { field1: 1 });
let encoded_a = bincode::serialize(&a).unwrap();
let decoded_a = bincode::deserialize(&encoded_a).unwrap();
assert_eq!(a, decoded_a);
let a = SchemaA::AV2(AV2 {
field1: 1,
field2: Some(42),
});
let encoded_a = bincode::serialize(&a).unwrap();
let decoded_a = bincode::deserialize(&encoded_a).unwrap();
assert_eq!(a, decoded_a);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment