Created
November 7, 2024 06:36
-
-
Save Akanoa/1e040648c01ea510a5ebb048c4a26cef to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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