Skip to content

Commit dc8d441

Browse files
sandreimacatangiu
authored andcommitted
Update readme and docs.
Signed-off-by: Andrei Sandu <[email protected]>
1 parent c26e5cd commit dc8d441

File tree

2 files changed

+260
-8
lines changed

2 files changed

+260
-8
lines changed

README.md

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,83 @@
1-
# versionize_derive
1+
**Versionize is a framework for version tolerant serializion/deserialization of
2+
Rust data structures, designed for usecases that need fast deserialization
3+
times and minimal size overhead. It does not aim to be a generic serialization
4+
framework and only the [bincode](https://crates.io/crates/bincode) backend is
5+
supported.**
6+
7+
#### You may be looking for:
8+
- [Versionize documentation](https://docs.rs/versionize/)
9+
- [Releases](https://github.com/firecracker-microvm/versionize_derive/releases)
210

311
## Important note
412

5-
This crate is currently used for cross-version serialization with the [Firecracker snapshot-restore dev preview](https://github.com/firecracker-microvm/firecracker/tree/v0.23.0), but has not been tested for other use cases. It should be considered **experimental software** outside the Firecracker context. It’s likely that this crate will see both interface and implementation changes in the future.
13+
This crate is currently used for cross-version serialization with the
14+
[Firecracker snapshot-restore dev preview][1], but has not been tested for
15+
other use cases. It should be considered **experimental software** outside the
16+
Firecracker context. It’s likely that this crate will see both interface and
17+
implementation changes in the future.
18+
19+
## Versionize in action
20+
21+
```rust
22+
extern crate versionize;
23+
extern crate versionize_derive;
24+
25+
use versionize::{VersionMap, Versionize, VersionizeResult};
26+
use versionize_derive::Versionize;
27+
28+
// The test structure is at version 3.
29+
#[derive(Debug, PartialEq, Versionize)]
30+
pub struct Test {
31+
a: u32,
32+
#[version(start = 2, end = 3)]
33+
b: u8,
34+
#[version(start = 3, default_fn = "default_c")]
35+
c: String,
36+
}
37+
38+
impl Test {
39+
// Default value for field `c`.
40+
// The callback is invoked when deserialization from and older version
41+
// where the field did not exist.
42+
fn default_c(_source_version: u16) -> String {
43+
"test_string".to_owned()
44+
}
45+
}
46+
47+
// Memory to hold the serialization output.
48+
let mut mem = vec![0u8; 512];
49+
// Create a new version map - it will start at version 1.
50+
let mut version_map = VersionMap::new();
51+
// Add new version and mark changes for Test struct: Set the current version
52+
// to point to Test struct version 2.
53+
version_map
54+
.new_version()
55+
.set_type_version(Test::type_id(), 2)
56+
.new_version()
57+
.set_type_version(Test::type_id(), 3);
58+
59+
let test_struct = Test {
60+
a: 1337,
61+
b: 0xFF,
62+
c: "c_value".to_owned(),
63+
};
64+
65+
// Serialize to version 2 - field c will not be serialized.
66+
test_struct
67+
.serialize(&mut mem.as_mut_slice(), &version_map, 2)
68+
.unwrap();
69+
70+
// Deserialize from version 2 - c should contain the default_fn() return value.
71+
let restored_test_struct = Test::deserialize(&mut mem.as_slice(), &version_map, 2).unwrap();
672

7-
## Description
73+
assert_eq!(
74+
restored_test_struct,
75+
Test {
76+
a: 1337,
77+
b: 255,
78+
c: "test_string".to_owned()
79+
}
80+
);
81+
```
882

9-
Exports the Versionize derive proc macro that generates the Versionize implementation for structs and enums by using annotations.
83+
[1]: https://github.com/firecracker-microvm/firecracker/tree/v0.24.0

src/lib.rs

Lines changed: 182 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,24 @@
22
// SPDX-License-Identifier: Apache-2.0
33
#![deny(missing_docs)]
44

5-
//! Exports the Versionize derive proc macro that generates the Versionize implementation
6-
//! for structs, and enums by using annotations.
7-
5+
//! Exports the `Versionize` derive proc macro that generates the Versionize
6+
//! trait implementation.
7+
//!
8+
//! Versionize generates serialization and deserialization code only for
9+
//! structures and enums.
10+
//!
11+
//! Supported primitives: u8, u16, u32, u64, usize, i8, i16, i32, i64, isize,
12+
//! char, f32, f64, String, Vec<T>, Arrays up to 32 elements, Box<T>,
13+
//! Wrapping<T>, Option<T>, FamStructWrapper<T>, and (T, U).
14+
//!
15+
//! Known issues and limitations:
16+
//! - Union serialization is not supported via the `Versionize` proc macro.
17+
//! - Implementing Versionize for non-repr(C) unions can result in undefined
18+
//! behaviour and MUST be avoided.
19+
//! - Versionize trait implementations for repr(C) unions must be backed by
20+
//! extensive testing.
21+
//! - Semantic serialization and deserialization is available only for structures.
22+
//!
823
extern crate proc_macro;
924
extern crate proc_macro2;
1025
extern crate quote;
@@ -30,7 +45,170 @@ pub(crate) const SEMANTIC_DE_FN: &str = "de_fn";
3045
pub(crate) const START_VERSION: &str = "start";
3146
pub(crate) const END_VERSION: &str = "end";
3247

33-
/// Implements the derive proc macro.
48+
/// Generates serialization and deserialization code as an implementation of
49+
/// the `Versionize` trait.
50+
///
51+
/// Different code paths are generated for each version of the structure or
52+
/// enum. There is no limit enforced on the maximum number of structure
53+
/// versions.
54+
///
55+
/// ### Struct and enum requirements
56+
/// - all members or enum variants need to implement the `Versionize` trait
57+
/// - no generics are being used (this is currenly a limitation)
58+
///
59+
/// ## Annotations
60+
///
61+
/// To facilitate version tolerant serialization "history metadata" is attached
62+
/// to the structure or enum. This is done by using the `version` attribute in
63+
/// their definition. In the below example a new field is added to the
64+
/// structure starting with version 2: `#[version(start = 2)]`.
65+
///
66+
/// ```ignore
67+
/// extern crate versionize;
68+
/// extern crate versionize_derive;
69+
/// use versionize::{Versionize, VersionizeError, VersionizeResult};
70+
/// use versionize_derive::Versionize;
71+
///
72+
/// #[derive(Versionize)]
73+
/// struct Test {
74+
/// a: u32,
75+
/// #[version(start = 2)]
76+
/// b: u8,
77+
/// }
78+
/// ```
79+
///
80+
/// Multiple version annotations can be defined for a field, like for example:
81+
/// `#[version(start = 2, end = 3)]`. Field was added in structure version 2
82+
/// and removed in version 3. The generated code will attempt to (de)serialize
83+
/// this field only for version 2 of the structure.
84+
///
85+
/// ### Supported field attributes and usage
86+
///
87+
/// The `version` attribute accepts multiple key/value pairs to be specified in
88+
/// order to support versioning, semantic serialization and default values for
89+
/// fields. All of these are optional and a default behaviour is provided in
90+
/// their absence.
91+
///
92+
/// #### default_fn
93+
///
94+
/// Provides an initialization value for a field when deserializing from an
95+
/// older structure version which does not contain this field. If not specified
96+
/// the `Default` trait isused to initialize the field.
97+
///
98+
/// ```ignore
99+
/// extern crate versionize;
100+
/// extern crate versionize_derive;
101+
/// use versionize::{Versionize, VersionizeError, VersionizeResult};
102+
/// use versionize_derive::Versionize;
103+
///
104+
/// #[derive(Versionize)]
105+
/// struct TestStruct {
106+
/// a: u32,
107+
/// #[version(start = 2, default_fn = "default_b")]
108+
/// b: u8,
109+
/// }
110+
///
111+
/// impl TestStruct {
112+
/// fn default_b(_source_version: u16) -> u8 {
113+
/// 12u8
114+
/// }
115+
/// }
116+
/// ```
117+
///
118+
/// The function name needs to be specified as a string and its prototype must
119+
/// take an u16 source version parameter and return a value of the same type as
120+
/// as the field.
121+
///
122+
/// #### start/end
123+
///
124+
/// Defines the field version lifetime. Fields can be added by specifing the
125+
/// start version of the structure when first defining them and can be later
126+
/// on removed from serialization logic by adding and end version.
127+
///
128+
/// For example: `#[version(start = 2, end = 4)]`. The field would be present
129+
/// in the structure v2 and v3, but starting with v4 it would no longer be
130+
/// serialized or deserialized.
131+
///
132+
/// Once a field is removed, it can never be added again in a future version.
133+
///
134+
/// #### ser_fn
135+
/// * Not supported for enums. *
136+
///
137+
/// Defines a semantic serialization function for a field. The function needs
138+
/// to be specified as a string and implemented as a method attached to
139+
/// the structure. The prototype of the function is
140+
/// `fn(&mut self, u16) -> VersionizeResult<()>`.
141+
///
142+
/// If defined, the method is called when the field is skipped from
143+
/// serialization because it does not exist in the target version of the
144+
/// structure. Its implementation can perform any mutation of `self` or return
145+
/// an error to stop serialization. Intended usage is to implement semantic
146+
/// translation or semantic validations.
147+
///
148+
/// ```ignore
149+
/// extern crate versionize;
150+
/// extern crate versionize_derive;
151+
/// use versionize::{Versionize, VersionizeError, VersionizeResult};
152+
/// use versionize_derive::Versionize;
153+
///
154+
/// #[derive(Versionize)]
155+
/// struct SomeStruct {
156+
/// some_u32: u32,
157+
/// #[version(start = 2, ser_fn = "ser_u16")]
158+
/// some_u16: u16,
159+
/// }
160+
///
161+
/// impl SomeStruct {
162+
/// fn ser_u16(&mut self, target_version: u16) -> VersionizeResult<()> {
163+
/// self.some_u32 = self.some_u32 & self.some_u16 as u32;
164+
/// Ok(())
165+
/// }
166+
/// }
167+
/// ```
168+
///
169+
/// #### de_fn
170+
/// * Not supported for enums. *
171+
///
172+
/// Defines a semantic deserialization function for a field. The function needs
173+
/// to be specified as a string and implemented as a method attached to
174+
/// the structure. The prototype of the function is
175+
/// `fn(&mut self, u16) -> VersionizeResult<()>`.
176+
///
177+
/// If defined, the method is called if the field is skipped from
178+
/// deserialization because it does not exist in the source version of the
179+
/// serialized structure. Its implementation can perform any mutation of `self`
180+
/// or return an error to stop deserialization. Intended usage is to implement
181+
/// semantic translation or semantic validations.
182+
///
183+
/// Both `default_fn` and `de_fn` can be specified for a field. `default_fn` is
184+
/// always called first and `de_fn` last.
185+
///
186+
/// ```ignore
187+
/// extern crate versionize;
188+
/// extern crate versionize_derive;
189+
/// use versionize::{Versionize, VersionizeError, VersionizeResult};
190+
/// use versionize_derive::Versionize;
191+
///
192+
/// #[derive(Clone, Versionize)]
193+
/// struct SomeStruct {
194+
/// some_u32: u32,
195+
/// #[version(start = 2, ser_fn = "ser_u16", de_fn = "de_u16")]
196+
/// some_u16: u16,
197+
/// }
198+
///
199+
/// impl SomeStruct {
200+
/// fn ser_u16(&mut self, target_version: u16) -> VersionizeResult<()> {
201+
/// self.some_u32 = self.some_u32 & self.some_u16 as u32;
202+
/// Ok(())
203+
/// }
204+
/// fn de_u16(&mut self, source_version: u16) -> VersionizeResult<()> {
205+
/// if source_version < 2 {
206+
/// self.some_u16 = (self.some_u32 & 0xFF) as u16;
207+
/// }
208+
/// Ok(())
209+
/// }
210+
/// }
211+
/// ```
34212
#[proc_macro_derive(Versionize, attributes(version))]
35213
pub fn impl_versionize(input: TokenStream) -> proc_macro::TokenStream {
36214
let input = parse_macro_input!(input as DeriveInput);

0 commit comments

Comments
 (0)