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
// 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 implementation
//! for structs, and enums by using annotations.

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";

/// Implements the derive proc macro.
#[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()
}