versionize_derive/lib.rs
1// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3#![deny(missing_docs)]
4
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
23extern crate proc_macro;
24extern crate proc_macro2;
25extern crate quote;
26extern crate syn;
27
28mod common;
29mod descriptors;
30mod fields;
31mod helpers;
32
33use common::Descriptor;
34use descriptors::{enum_desc::EnumDescriptor, struct_desc::StructDescriptor};
35use proc_macro::TokenStream;
36use quote::quote;
37use syn::{parse_macro_input, DeriveInput};
38
39pub(crate) const ATTRIBUTE_NAME: &str = "version";
40
41/// Struct annotation constants.
42pub(crate) const DEFAULT_FN: &str = "default_fn";
43pub(crate) const SEMANTIC_SER_FN: &str = "ser_fn";
44pub(crate) const SEMANTIC_DE_FN: &str = "de_fn";
45pub(crate) const START_VERSION: &str = "start";
46pub(crate) const END_VERSION: &str = "end";
47
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/// ```
212#[proc_macro_derive(Versionize, attributes(version))]
213pub fn impl_versionize(input: TokenStream) -> proc_macro::TokenStream {
214 let input = parse_macro_input!(input as DeriveInput);
215 let ident = input.ident.clone();
216 let generics = input.generics.clone();
217
218 let descriptor: Box<dyn Descriptor> = match &input.data {
219 syn::Data::Struct(data_struct) => {
220 Box::new(StructDescriptor::new(data_struct, ident.clone()))
221 }
222 syn::Data::Enum(data_enum) => Box::new(EnumDescriptor::new(data_enum, ident.clone())),
223 syn::Data::Union(_) => {
224 return (quote! {
225 compile_error!("Union serialization is not supported.");
226 })
227 .into()
228 }
229 };
230
231 let version = descriptor.version();
232 let versioned_serializer = descriptor.generate_serializer();
233 let deserializer = descriptor.generate_deserializer();
234 let serializer = quote! {
235 // Get the struct version for the input app_version.
236 let version = version_map.get_type_version(app_version, <Self as Versionize>::type_id());
237 // We will use this copy to perform semantic serialization.
238 let mut copy_of_self = self.clone();
239 match version {
240 #versioned_serializer
241 _ => panic!("Unknown {:?} version {}.", &<Self as Versionize>::type_id(), version)
242 }
243 };
244 (quote! {
245 impl Versionize for #ident #generics {
246 fn serialize<W: std::io::Write>(&self, writer: &mut W, version_map: &VersionMap, app_version: u16) -> VersionizeResult<()> {
247 #serializer
248 Ok(())
249 }
250
251 fn deserialize<R: std::io::Read>(mut reader: &mut R, version_map: &VersionMap, app_version: u16) -> VersionizeResult<Self> {
252 #deserializer
253 }
254
255 // Returns struct current version.
256 fn version() -> u16 {
257 #version
258 }
259 }
260 }).into()
261}