Derive Macro versionize_derive::Versionize

source ·
#[derive(Versionize)]
{
    // Attributes available to this derive:
    #[version]
}
Expand description

Generates serialization and deserialization code as an implementation of the Versionize trait.

Different code paths are generated for each version of the structure or enum. There is no limit enforced on the maximum number of structure versions.

Struct and enum requirements

  • all members or enum variants need to implement the Versionize trait
  • no generics are being used (this is currenly a limitation)

Annotations

To facilitate version tolerant serialization “history metadata” is attached to the structure or enum. This is done by using the version attribute in their definition. In the below example a new field is added to the structure starting with version 2: #[version(start = 2)].

extern crate versionize;
extern crate versionize_derive;
use versionize::{Versionize, VersionizeError, VersionizeResult};
use versionize_derive::Versionize;

#[derive(Versionize)]
struct Test {
    a: u32,
    #[version(start = 2)]
    b: u8,
}

Multiple version annotations can be defined for a field, like for example: #[version(start = 2, end = 3)]. Field was added in structure version 2 and removed in version 3. The generated code will attempt to (de)serialize this field only for version 2 of the structure.

Supported field attributes and usage

The version attribute accepts multiple key/value pairs to be specified in order to support versioning, semantic serialization and default values for fields. All of these are optional and a default behaviour is provided in their absence.

default_fn

Provides an initialization value for a field when deserializing from an older structure version which does not contain this field. If not specified the Default trait isused to initialize the field.

extern crate versionize;
extern crate versionize_derive;
use versionize::{Versionize, VersionizeError, VersionizeResult};
use versionize_derive::Versionize;

#[derive(Versionize)]
struct TestStruct {
    a: u32,
    #[version(start = 2, default_fn = "default_b")]
    b: u8,
}

impl TestStruct {
    fn default_b(_source_version: u16) -> u8 {
        12u8
    }
}

The function name needs to be specified as a string and its prototype must take an u16 source version parameter and return a value of the same type as as the field.

start/end

Defines the field version lifetime. Fields can be added by specifing the start version of the structure when first defining them and can be later on removed from serialization logic by adding and end version.

For example: #[version(start = 2, end = 4)]. The field would be present in the structure v2 and v3, but starting with v4 it would no longer be serialized or deserialized.

Once a field is removed, it can never be added again in a future version.

ser_fn
  • Not supported for enums. *

Defines a semantic serialization function for a field. The function needs to be specified as a string and implemented as a method attached to the structure. The prototype of the function is fn(&mut self, u16) -> VersionizeResult<()>.

If defined, the method is called when the field is skipped from serialization because it does not exist in the target version of the structure. Its implementation can perform any mutation of self or return an error to stop serialization. Intended usage is to implement semantic translation or semantic validations.

extern crate versionize;
extern crate versionize_derive;
use versionize::{Versionize, VersionizeError, VersionizeResult};
use versionize_derive::Versionize;

#[derive(Versionize)]
struct SomeStruct {
    some_u32: u32,
    #[version(start = 2, ser_fn = "ser_u16")]
    some_u16: u16,
}

impl SomeStruct {
    fn ser_u16(&mut self, target_version: u16) -> VersionizeResult<()> {
        self.some_u32 = self.some_u32 & self.some_u16 as u32;
        Ok(())
    }
}
de_fn
  • Not supported for enums. *

Defines a semantic deserialization function for a field. The function needs to be specified as a string and implemented as a method attached to the structure. The prototype of the function is fn(&mut self, u16) -> VersionizeResult<()>.

If defined, the method is called if the field is skipped from deserialization because it does not exist in the source version of the serialized structure. Its implementation can perform any mutation of self or return an error to stop deserialization. Intended usage is to implement semantic translation or semantic validations.

Both default_fn and de_fn can be specified for a field. default_fn is always called first and de_fn last.

extern crate versionize;
extern crate versionize_derive;
use versionize::{Versionize, VersionizeError, VersionizeResult};
use versionize_derive::Versionize;

#[derive(Clone, Versionize)]
struct SomeStruct {
    some_u32: u32,
    #[version(start = 2, ser_fn = "ser_u16", de_fn = "de_u16")]
    some_u16: u16,
}

impl SomeStruct {
    fn ser_u16(&mut self, target_version: u16) -> VersionizeResult<()> {
        self.some_u32 = self.some_u32 & self.some_u16 as u32;
        Ok(())
    }
    fn de_u16(&mut self, source_version: u16) -> VersionizeResult<()> {
        if source_version < 2 {
            self.some_u16 = (self.some_u32 & 0xFF) as u16;
        }
        Ok(())
    }
}