Skip to content

Enhance cross-version support #23

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ keywords = ["serialization", "version"]
proc-macro2 = "1.0.47"
quote = "1.0.21"
syn = { version = "1.0.13", features=["full", "extra-traits"]}
semver = "1.0.16"

[lib]
proc-macro = true
Expand Down
83 changes: 46 additions & 37 deletions src/common.rs
Original file line number Diff line number Diff line change
@@ -1,67 +1,76 @@
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use std::collections::BTreeMap;

/// An interface for generating serialzer and deserializers based on
/// field descriptions.
pub trait Descriptor {
/// Returns the serializer code block as a token stream.
fn generate_serializer(&self) -> proc_macro2::TokenStream;
/// Returns the deserializer code block as a token stream.
fn generate_deserializer(&self) -> proc_macro2::TokenStream;
/// Returns the curent version.
fn version(&self) -> u16;
/// Returns the type name as string.
fn ty(&self) -> String;
}

/// Describes a structure and it's fields.
pub(crate) struct GenericDescriptor<T> {
// The structure type identifier.
pub ty: syn::Ident,
pub version: u16,
pub versions: BTreeMap<u64, Vec<u64>>,
pub fields: Vec<T>,
}

// A trait that defines an interface to check if a certain field
// exists at a specified version.
pub(crate) trait Exists {
fn exists_at(&self, version: u16) -> bool {
// All fields have a start version.
// Some field do not have an end version specified.
version >= self.start_version()
&& (0 == self.end_version() || (self.end_version() > 0 && version < self.end_version()))
fn exists_at(&self, minor: u64, patch: u64) -> bool {
let start = self.start_version();
let end = self.end_version();

let default_start = || -> bool {
if start.is_empty() {
return true;
} else if start.iter().all(|x| minor < x.minor) {
return false;
} else if start.iter().all(|x| minor > x.minor) {
return true;
}
return false;
};
let default_end = || -> bool {
if end.is_empty() {
return true;
} else if end.iter().all(|x| minor < x.minor) {
return true;
} else if end.iter().all(|x| minor > x.minor) {
return false;
}
return false;
};

start
.iter()
.find(|list| list.minor == minor)
.map_or_else(default_start, |found| patch >= found.patch)
&& end
.iter()
.find(|list| list.minor == minor)
.map_or_else(default_end, |found| patch < found.patch)
}

fn list_versions(&self) -> Vec<semver::Version> {
let mut rets = self.start_version().to_owned();
rets.append(&mut self.end_version().to_owned());
rets.sort();
rets.dedup();
rets
}

fn start_version(&self) -> u16;
fn end_version(&self) -> u16;
fn start_version(&self) -> &[semver::Version];
fn end_version(&self) -> &[semver::Version];
}

// A trait that defines an interface for exposing a field type.
pub(crate) trait FieldType {
fn ty(&self) -> syn::Type;
}

#[cfg(test)]
mod tests {
use super::Exists;

#[test]
fn test_exists_at() {
impl Exists for u32 {
fn start_version(&self) -> u16 {
3
}

fn end_version(&self) -> u16 {
5
}
}

let test = 1234;
assert!(!test.exists_at(2));
assert!(test.exists_at(3));
assert!(test.exists_at(4));
assert!(!test.exists_at(5));
assert!(!test.exists_at(6));
}
}
45 changes: 15 additions & 30 deletions src/descriptors/enum_desc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,71 +3,57 @@

use common::{Descriptor, GenericDescriptor};
use fields::enum_variant::*;
use helpers::compute_version;
use helpers::collect_version;
use quote::quote;
use std::collections::BTreeMap;

pub(crate) type EnumDescriptor = GenericDescriptor<EnumVariant>;

impl Descriptor for EnumDescriptor {
fn generate_serializer(&self) -> proc_macro2::TokenStream {
let mut versioned_serializers = proc_macro2::TokenStream::new();

for i in 1..=self.version {
let mut versioned_serializer = proc_macro2::TokenStream::new();
for field in &self.fields {
versioned_serializers.extend(field.generate_serializer(u64::MAX, u64::MAX));
}

for field in &self.fields {
versioned_serializer.extend(field.generate_serializer(i));
// Generate the serializer for current version only.
quote! {
match self {
#versioned_serializers
}

// Generate the match arm for version `i` serializer.
versioned_serializers.extend(quote! {
#i => {
match self {
#versioned_serializer
}
}
});
}

versioned_serializers
}

// Versioned/semantic deserialization is not implemented for enums.
fn generate_deserializer(&self) -> proc_macro2::TokenStream {
let mut versioned_deserializers = proc_macro2::TokenStream::new();

for field in &self.fields {
versioned_deserializers.extend(field.generate_deserializer());
versioned_deserializers.extend(field.generate_deserializer(u64::MAX, u64::MAX));
}

quote! {
let variant_index = <u32 as Versionize>::deserialize(&mut reader, version_map, app_version)?;
let source = version_map.get_crate_version(env!("CARGO_PKG_NAME"))?;
let variant_index = <u32 as dbs_versionize::Versionize>::deserialize(&mut reader, version_map)?;
match variant_index {
#versioned_deserializers
x => return Err(VersionizeError::Deserialize(format!("Unknown variant_index {}", x)))
}
}
}

fn version(&self) -> u16 {
self.version
}

fn ty(&self) -> String {
self.ty.to_string()
}
}

impl EnumDescriptor {
pub fn new(input: &syn::DataEnum, ident: syn::Ident) -> Self {
let mut descriptor = EnumDescriptor {
ty: ident,
version: 1,
versions: BTreeMap::new(),
fields: vec![],
};

descriptor.parse_enum_variants(&input.variants);
descriptor.version = compute_version(&descriptor.fields);
descriptor.versions = collect_version(&descriptor.fields);
descriptor
}

Expand All @@ -76,8 +62,7 @@ impl EnumDescriptor {
variants: &syn::punctuated::Punctuated<syn::Variant, syn::token::Comma>,
) {
for (index, variant) in variants.iter().enumerate() {
self.fields
.push(EnumVariant::new(self.version, variant, index as u32));
self.fields.push(EnumVariant::new(variant, index as u32));
}
}
}
Loading