Skip to content

Commit

Permalink
backwards compatible call_type creation_method (#11450)
Browse files Browse the repository at this point in the history
* rlp_derive: update syn & co

* rlp_derive: remove dummy_const

* rlp_derive: remove unused attirubutes

* rlp-derive: change authors

* rlp_derive: add rlp(default) attribute

* Revert "Revert "[Trace] Distinguish between `create` and `create2` (#11311)" (#11427)"

This reverts commit 5d4993b0f856bf9e0e2c78849b72e581f0cde686.

* trace: backwards compatible call_type and creation_method

* trace: add rlp backward compatibility tests

* cleanup

* i know, i hate backwards compatibility too

* address review grumbles
  • Loading branch information
ordian authored Feb 5, 2020
1 parent 96d0d48 commit 66af133
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 18 deletions.
59 changes: 48 additions & 11 deletions src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,17 @@ pub fn impl_decodable(ast: &syn::DeriveInput) -> TokenStream {
_ => panic!("#[derive(RlpDecodable)] is only defined for structs."),
};

let mut default_attribute_encountered = false;
let stmts: Vec<_> = body
.fields
.iter()
.enumerate()
.map(decodable_field_map)
.collect();
.map(|(i, field)| decodable_field(
i,
field,
decodable_parse_quotes(),
&mut default_attribute_encountered,
)).collect();
let name = &ast.ident;

let impl_block = quote! {
Expand Down Expand Up @@ -83,7 +88,13 @@ pub fn impl_decodable_wrapper(ast: &syn::DeriveInput) -> TokenStream {
let fields: Vec<_> = body.fields.iter().collect();
if fields.len() == 1 {
let field = fields.first().expect("fields.len() == 1; qed");
decodable_field(0, field, decodable_wrapper_parse_quotes())
let mut default_attribute_encountered = false;
decodable_field(
0,
field,
decodable_wrapper_parse_quotes(),
&mut default_attribute_encountered,
)
} else {
panic!("#[derive(RlpEncodableWrapper)] is only defined for structs with one field.")
}
Expand Down Expand Up @@ -111,11 +122,12 @@ pub fn impl_decodable_wrapper(ast: &syn::DeriveInput) -> TokenStream {
}
}

fn decodable_field_map(tuple: (usize, &syn::Field)) -> TokenStream {
decodable_field(tuple.0, tuple.1, decodable_parse_quotes())
}

fn decodable_field(index: usize, field: &syn::Field, quotes: ParseQuotes) -> TokenStream {
fn decodable_field(
index: usize,
field: &syn::Field,
quotes: ParseQuotes,
default_attribute_encountered: &mut bool,
) -> TokenStream {
let id = match field.ident {
Some(ref ident) => quote! { #ident },
None => {
Expand All @@ -124,11 +136,27 @@ fn decodable_field(index: usize, field: &syn::Field, quotes: ParseQuotes) -> Tok
}
};

let index = index - *default_attribute_encountered as usize;
let index = quote! { #index };

let single = quotes.single;
let list = quotes.list;

let attributes = &field.attrs;
let default = if let Some(attr) = attributes.iter().find(|attr| attr.path.is_ident("rlp")) {
if *default_attribute_encountered {
panic!("only 1 #[rlp(default)] attribute is allowed in a struct")
}
match attr.parse_args() {
Ok(proc_macro2::TokenTree::Ident(ident)) if ident.to_string() == "default" => {},
_ => panic!("only #[rlp(default)] attribute is supported"),
}
*default_attribute_encountered = true;
true
} else {
false
};

match field.ty {
syn::Type::Path(ref path) => {
let ident = &path
Expand All @@ -137,15 +165,24 @@ fn decodable_field(index: usize, field: &syn::Field, quotes: ParseQuotes) -> Tok
.first()
.expect("there must be at least 1 segment")
.ident;
if &ident.to_string() == "Vec" {
let ident_type = ident.to_string();
if &ident_type == "Vec" {
if quotes.takes_index {
quote! { #id: #list(#index)?, }
if default {
quote! { #id: #list(#index).unwrap_or_default(), }
} else {
quote! { #id: #list(#index)?, }
}
} else {
quote! { #id: #list()?, }
}
} else {
if quotes.takes_index {
quote! { #id: #single(#index)?, }
if default {
quote! { #id: #single(#index).unwrap_or_default(), }
} else {
quote! { #id: #single(#index)?, }
}
} else {
quote! { #id: #single()?, }
}
Expand Down
6 changes: 1 addition & 5 deletions src/en.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pub fn impl_encodable(ast: &syn::DeriveInput) -> TokenStream {
.fields
.iter()
.enumerate()
.map(encodable_field_map)
.map(|(i, field)| encodable_field(i, field))
.collect();
let name = &ast.ident;

Expand Down Expand Up @@ -84,10 +84,6 @@ pub fn impl_encodable_wrapper(ast: &syn::DeriveInput) -> TokenStream {
}
}

fn encodable_field_map(tuple: (usize, &syn::Field)) -> TokenStream {
encodable_field(tuple.0, tuple.1)
}

fn encodable_field(index: usize, field: &syn::Field) -> TokenStream {
let ident = match field.ident {
Some(ref ident) => quote! { #ident },
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use de::{impl_decodable, impl_decodable_wrapper};
use en::{impl_encodable, impl_encodable_wrapper};
use proc_macro::TokenStream;

#[proc_macro_derive(RlpEncodable)]
#[proc_macro_derive(RlpEncodable, attributes(rlp))]
pub fn encodable(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
let gen = impl_encodable(&ast);
Expand All @@ -37,7 +37,7 @@ pub fn encodable_wrapper(input: TokenStream) -> TokenStream {
gen.into()
}

#[proc_macro_derive(RlpDecodable)]
#[proc_macro_derive(RlpDecodable, attributes(rlp))]
pub fn decodable(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
let gen = impl_decodable(&ast);
Expand Down
27 changes: 27 additions & 0 deletions tests/rlp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,30 @@ fn test_encode_foo_wrapper() {
let decoded = decode(&expected).expect("decode failure");
assert_eq!(foo, decoded);
}

#[test]
fn test_encode_foo_default() {
#[derive(Debug, PartialEq, RlpEncodable, RlpDecodable)]
struct FooDefault {
a: String,
/// It works with other attributes.
#[rlp(default)]
b: Option<Vec<u8>>,
}

let attack_of = String::from("clones");
let foo = Foo { a: attack_of.clone() };

let expected = vec![0xc7, 0x86, b'c', b'l', b'o', b'n', b'e', b's'];
let out = encode(&foo);
assert_eq!(out, expected);

let foo_default = FooDefault { a: attack_of.clone(), b: None };

let decoded = decode(&expected).expect("default failure");
assert_eq!(foo_default, decoded);

let foo_some = FooDefault { a: attack_of.clone(), b: Some(vec![1, 2, 3]) };
let out = encode(&foo_some);
assert_eq!(decode(&out), Ok(foo_some));
}

0 comments on commit 66af133

Please sign in to comment.