1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 #![deny(missing_docs)] //! Exports the `Versionize` derive proc macro that generates the Versionize //! trait implementation. //! //! Versionize generates serialization and deserialization code only for //! structures and enums. //! //! Supported primitives: u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, //! char, f32, f64, String, Vec<T>, Arrays up to 32 elements, Box<T>, //! Wrapping<T>, Option<T>, FamStructWrapper<T>, and (T, U). //! //! Known issues and limitations: //! - Union serialization is not supported via the `Versionize` proc macro. //! - Implementing Versionize for non-repr(C) unions can result in undefined //! behaviour and MUST be avoided. //! - Versionize trait implementations for repr(C) unions must be backed by //! extensive testing. //! - Semantic serialization and deserialization is available only for structures. //! extern crate proc_macro; extern crate proc_macro2; extern crate quote; extern crate syn; mod common; mod descriptors; mod fields; mod helpers; use common::Descriptor; use descriptors::{enum_desc::EnumDescriptor, struct_desc::StructDescriptor}; use proc_macro::TokenStream; use quote::quote; use syn::{parse_macro_input, DeriveInput}; pub(crate) const ATTRIBUTE_NAME: &str = "version"; /// Struct annotation constants. pub(crate) const DEFAULT_FN: &str = "default_fn"; pub(crate) const SEMANTIC_SER_FN: &str = "ser_fn"; pub(crate) const SEMANTIC_DE_FN: &str = "de_fn"; pub(crate) const START_VERSION: &str = "start"; pub(crate) const END_VERSION: &str = "end"; /// 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)]`. /// /// ```ignore /// 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. /// /// ```ignore /// 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. /// /// ```ignore /// 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. /// /// ```ignore /// 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(()) /// } /// } /// ``` #[proc_macro_derive(Versionize, attributes(version))] pub fn impl_versionize(input: TokenStream) -> proc_macro::TokenStream { let input = parse_macro_input!(input as DeriveInput); let ident = input.ident.clone(); let generics = input.generics.clone(); let descriptor: Box<dyn Descriptor> = match &input.data { syn::Data::Struct(data_struct) => { Box::new(StructDescriptor::new(&data_struct, ident.clone())) } syn::Data::Enum(data_enum) => Box::new(EnumDescriptor::new(&data_enum, ident.clone())), syn::Data::Union(_) => { return (quote! { compile_error!("Union serialization is not supported."); }) .into() } }; let version = descriptor.version(); let versioned_serializer = descriptor.generate_serializer(); let deserializer = descriptor.generate_deserializer(); let serializer = quote! { // Get the struct version for the input app_version. let version = version_map.get_type_version(app_version, <Self as Versionize>::type_id()); // We will use this copy to perform semantic serialization. let mut copy_of_self = self.clone(); match version { #versioned_serializer _ => panic!("Unknown {:?} version {}.", &<Self as Versionize>::type_id(), version) } }; (quote! { impl Versionize for #ident #generics { fn serialize<W: std::io::Write>(&self, writer: &mut W, version_map: &VersionMap, app_version: u16) -> VersionizeResult<()> { #serializer Ok(()) } fn deserialize<R: std::io::Read>(mut reader: &mut R, version_map: &VersionMap, app_version: u16) -> VersionizeResult<Self> { #deserializer } // Returns struct current version. fn version() -> u16 { #version } } }).into() }