[−][src]Crate serde_version
Serde version
Versioning support for serde.
When software are developped and used at the same time the data formats may change from one version to another and persisting data may be produced by a specific version and loaded by another version.
Serde version provide a versioning feature for serde for the main use cases.
Note 1: Requires the specialization feature.
Note 2: Use the derive
feature to generate the DeserializeVersioned
implementation
Goals of Serde version
We aim at solving the case were a type or a set of types in a deserializer's data needs to be upgraded to their latest format. This is the case when a mandatory property was added or removed, or an existing property changed.
Note: There already is support for added optional properties in serde.
(Use the default
feature of serde)
Example:
Let's have a file containing these structure with those version number:
A: 1, B: 1, C: 2
and the current version numbers are: A: 3, B: 2, C: 4
.
Then in latest code version, we have the former data structures versions,
let's call them: Av1
, Av2
, Bv1
, Cv1
, Cv2
, Cv3
.
Deserializing, whenever a structure A
, B
or C
is ran into,
then it is loaded with the appropriate format (in our case it will be Av1
, Bv1
and Cv2
)
and then converted to A
, B
or C
using the From trait.
Non goals
This is based on types that can be upgraded individually. Types that needs to be upgraded together is way more complex to handle and usually relies on domain specific deserializer.
So, these data format should be handle with specific Deserialize
traits implementations.
Unsupported Serde feature with versioning
deserialize_in_place
is not supported
Deserializing in place with versioning support is way more complicated, so we don't deal with this in this crate.
Not supported with deserialize_with
callback
You must take care of the versioning in your callback
Versioning is only supported for structs and enums
There is no use case where versioning tuples and the unit type is useful.
Usage
To describe the previous versions of a type, we use the #[versions(...)]
attribute along with
the DeserializeVersioned
trait.
Authoring example:
// Version 1 of struct A
// It must implement Deserialize, so it can be loaded by serde
#[derive(Deserialize)]
// It must be identified by A during deserialization
#[serde(rename = "A")]
struct Av1 {
a: u8
}
// Current version of struct A
// It must implement Deserialize and DeserializeVersioned
#[derive(Deserialize, DeserializeVersioned)]
// We use the versions attribute to define the previous versions
#[versions(v(index = 1, type = "Av1"), v(index = 2, type = "A"))]
// So, Version n°1 of A is Av1, Versions n°2 (current) of A is A
struct A {
// We moved a property
b: u8
}
// A must implement From for all previous type, so we implement From<Av1>
impl From<Av1> for A {
fn from(v: Av1) -> Self {
Self {
b: v.a
}
}
}
To perform the deserialization with the versioning support, we need to do two steps:
- Get the
VersionMap
which holds the version number to use per type - Call the
deserialize_versioned
method with theVersionMap
Note: The id used to find the version number of a type during deserialization is the deserialization name of the type.
Execution example:
#[derive(Deserialize, PartialEq, Debug)] struct AInMap { a: A, } fn main() { // Use ron as data format for this example use ron; use serde_version::DeserializeVersioned; // First get a header // Here, we use the version 1 of `A` // Note: `rust_out` is the module used for the doc script let versions: serde_version::DefaultVersionMap = ron::de::from_str(r#"{ "rust_out::A": 1 }"#).unwrap(); // Let's deserialize some values // Deserialize directly A let mut deserializer = ron::de::Deserializer::from_str(r#"A(a: 1)"#).unwrap(); let value = A::deserialize_versioned(&mut deserializer, &versions).unwrap(); assert_eq!(value, A { b: 1 }); // Deserialize A contained in a struct property let mut deserializer = ron::de::Deserializer::from_str(r#"AInMap(a: A(a: 2))"#).unwrap(); // Note: All types implementing `Deserialize` will also implement `DeserializeVersioned` let value = AInMap::deserialize_versioned(&mut deserializer, &versions).unwrap(); assert_eq!(value.a, A { b: 2}); }
VersionedDeserializer
Under the hood, deserialize_version
wraps the provided deserializer with
the VersionedDeserializer
to support the versioning.
Modules
exports |
Structs
InvalidVersionError | Error used when a provided version number is not handled by current code |
VersionedDeserializer | A wrapper around a deserialize to support the deserialization. |
Enums
Error | Error wrapper to add the version number related errors |
Traits
DeserializeVersioned | Trait for versioning support during deserialization |
VersionMap | Maps the version number for each deserialization type name |
Type Definitions
DefaultVersionMap |