rlp-derive-0.1.0/CHANGELOG.md010064400017500001750000000003771362124426300136250ustar0000000000000000# Changelog The format is based on [Keep a Changelog]. [Keep a Changelog]: http://keepachangelog.com/en/1.0.0/ ## [Unreleased] ## [0.1.0] - 2020-02-13 - Extracted from parity-ethereum repo. [#343](https://github.com/paritytech/parity-common/pull/343) rlp-derive-0.1.0/Cargo.toml.orig010064400017500001750000000005651362124426300147020ustar0000000000000000[package] name = "rlp-derive" version = "0.1.0" authors = ["Parity Technologies "] license = "MIT/Apache-2.0" description = "Derive macro for #[derive(RlpEncodable, RlpDecodable)]" homepage = "http://parity.io" edition = "2018" [lib] proc-macro = true [dependencies] syn = "1.0.14" quote = "1.0.2" proc-macro2 = "1.0.8" [dev-dependencies] rlp = "0.4.4" rlp-derive-0.1.0/Cargo.toml0000644000000017001362124440700112000ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "rlp-derive" version = "0.1.0" authors = ["Parity Technologies "] description = "Derive macro for #[derive(RlpEncodable, RlpDecodable)]" homepage = "http://parity.io" license = "MIT/Apache-2.0" [lib] proc-macro = true [dependencies.proc-macro2] version = "1.0.8" [dependencies.quote] version = "1.0.2" [dependencies.syn] version = "1.0.14" [dev-dependencies.rlp] version = "0.4.4" rlp-derive-0.1.0/src/de.rs010064400017500001750000000077621362124426300135460ustar0000000000000000// Copyright 2020 Parity Technologies // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use proc_macro2::TokenStream; use quote::quote; struct ParseQuotes { single: TokenStream, list: TokenStream, takes_index: bool, } fn decodable_parse_quotes() -> ParseQuotes { ParseQuotes { single: quote! { rlp.val_at }, list: quote! { rlp.list_at }, takes_index: true } } fn decodable_wrapper_parse_quotes() -> ParseQuotes { ParseQuotes { single: quote! { rlp.as_val }, list: quote! { rlp.as_list }, takes_index: false } } pub fn impl_decodable(ast: &syn::DeriveInput) -> TokenStream { let body = match ast.data { syn::Data::Struct(ref s) => s, _ => panic!("#[derive(RlpDecodable)] is only defined for structs."), }; let mut default_attribute_encountered = false; let stmts: Vec<_> = body .fields .iter() .enumerate() .map(|(i, field)| decodable_field(i, field, decodable_parse_quotes(), &mut default_attribute_encountered)) .collect(); let name = &ast.ident; let impl_block = quote! { impl rlp::Decodable for #name { fn decode(rlp: &rlp::Rlp) -> Result { let result = #name { #(#stmts)* }; Ok(result) } } }; quote! { const _: () = { extern crate rlp; #impl_block }; } } pub fn impl_decodable_wrapper(ast: &syn::DeriveInput) -> TokenStream { let body = match ast.data { syn::Data::Struct(ref s) => s, _ => panic!("#[derive(RlpDecodableWrapper)] is only defined for structs."), }; let stmt = { let fields: Vec<_> = body.fields.iter().collect(); if fields.len() == 1 { let field = fields.first().expect("fields.len() == 1; qed"); 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.") } }; let name = &ast.ident; let impl_block = quote! { impl rlp::Decodable for #name { fn decode(rlp: &rlp::Rlp) -> Result { let result = #name { #stmt }; Ok(result) } } }; quote! { const _: () = { extern crate rlp; #impl_block }; } } 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 => { let index: syn::Index = index.into(); quote! { #index } } }; 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.path.segments.first().expect("there must be at least 1 segment").ident; let ident_type = ident.to_string(); if &ident_type == "Vec" { if quotes.takes_index { if default { quote! { #id: #list(#index).unwrap_or_default(), } } else { quote! { #id: #list(#index)?, } } } else { quote! { #id: #list()?, } } } else { if quotes.takes_index { if default { quote! { #id: #single(#index).unwrap_or_default(), } } else { quote! { #id: #single(#index)?, } } } else { quote! { #id: #single()?, } } } } _ => panic!("rlp_derive not supported"), } } rlp-derive-0.1.0/src/en.rs010064400017500001750000000056101362124426300135460ustar0000000000000000// Copyright 2020 Parity Technologies // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use proc_macro2::TokenStream; use quote::quote; pub fn impl_encodable(ast: &syn::DeriveInput) -> TokenStream { let body = match ast.data { syn::Data::Struct(ref s) => s, _ => panic!("#[derive(RlpEncodable)] is only defined for structs."), }; let stmts: Vec<_> = body.fields.iter().enumerate().map(|(i, field)| encodable_field(i, field)).collect(); let name = &ast.ident; let stmts_len = stmts.len(); let stmts_len = quote! { #stmts_len }; let impl_block = quote! { impl rlp::Encodable for #name { fn rlp_append(&self, stream: &mut rlp::RlpStream) { stream.begin_list(#stmts_len); #(#stmts)* } } }; quote! { const _: () = { extern crate rlp; #impl_block }; } } pub fn impl_encodable_wrapper(ast: &syn::DeriveInput) -> TokenStream { let body = match ast.data { syn::Data::Struct(ref s) => s, _ => panic!("#[derive(RlpEncodableWrapper)] is only defined for structs."), }; let stmt = { let fields: Vec<_> = body.fields.iter().collect(); if fields.len() == 1 { let field = fields.first().expect("fields.len() == 1; qed"); encodable_field(0, field) } else { panic!("#[derive(RlpEncodableWrapper)] is only defined for structs with one field.") } }; let name = &ast.ident; let impl_block = quote! { impl rlp::Encodable for #name { fn rlp_append(&self, stream: &mut rlp::RlpStream) { #stmt } } }; quote! { const _: () = { extern crate rlp; #impl_block }; } } fn encodable_field(index: usize, field: &syn::Field) -> TokenStream { let ident = match field.ident { Some(ref ident) => quote! { #ident }, None => { let index: syn::Index = index.into(); quote! { #index } } }; let id = quote! { self.#ident }; match field.ty { syn::Type::Path(ref path) => { let top_segment = path.path.segments.first().expect("there must be at least 1 segment"); let ident = &top_segment.ident; if &ident.to_string() == "Vec" { let inner_ident = match top_segment.arguments { syn::PathArguments::AngleBracketed(ref angle) => { let ty = angle.args.first().expect("Vec has only one angle bracketed type; qed"); match *ty { syn::GenericArgument::Type(syn::Type::Path(ref path)) => { &path.path.segments.first().expect("there must be at least 1 segment").ident } _ => panic!("rlp_derive not supported"), } } _ => unreachable!("Vec has only one angle bracketed type; qed"), }; quote! { stream.append_list::<#inner_ident, _>(&#id); } } else { quote! { stream.append(&#id); } } } _ => panic!("rlp_derive not supported"), } } rlp-derive-0.1.0/src/lib.rs010064400017500001750000000033361362124426300137150ustar0000000000000000// Copyright 2020 Parity Technologies // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Derive macro for `#[derive(RlpEncodable, RlpDecodable)]`. //! //! For example of usage see `./tests/rlp.rs`. //! //! This library also supports up to 1 `#[rlp(default)]` in a struct, //! which is similar to [`#[serde(default)]`](https://serde.rs/field-attrs.html#default) //! with the caveat that we use the `Default` value if //! the field deserialization fails, as we don't serialize field //! names and there is no way to tell if it is present or not. extern crate proc_macro; mod de; mod en; use de::{impl_decodable, impl_decodable_wrapper}; use en::{impl_encodable, impl_encodable_wrapper}; use proc_macro::TokenStream; #[proc_macro_derive(RlpEncodable, attributes(rlp))] pub fn encodable(input: TokenStream) -> TokenStream { let ast = syn::parse(input).unwrap(); let gen = impl_encodable(&ast); gen.into() } #[proc_macro_derive(RlpEncodableWrapper)] pub fn encodable_wrapper(input: TokenStream) -> TokenStream { let ast = syn::parse(input).unwrap(); let gen = impl_encodable_wrapper(&ast); gen.into() } #[proc_macro_derive(RlpDecodable, attributes(rlp))] pub fn decodable(input: TokenStream) -> TokenStream { let ast = syn::parse(input).unwrap(); let gen = impl_decodable(&ast); gen.into() } #[proc_macro_derive(RlpDecodableWrapper)] pub fn decodable_wrapper(input: TokenStream) -> TokenStream { let ast = syn::parse(input).unwrap(); let gen = impl_decodable_wrapper(&ast); gen.into() } rlp-derive-0.1.0/tests/rlp.rs010064400017500001750000000035711362124426300143200ustar0000000000000000// Copyright 2020 Parity Technologies // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use rlp::{decode, encode}; use rlp_derive::{RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper}; #[derive(Debug, PartialEq, RlpEncodable, RlpDecodable)] struct Foo { a: String, } #[derive(Debug, PartialEq, RlpEncodableWrapper, RlpDecodableWrapper)] struct FooWrapper { a: String, } #[test] fn test_encode_foo() { let foo = Foo { a: "cat".into() }; let expected = vec![0xc4, 0x83, b'c', b'a', b't']; let out = encode(&foo); assert_eq!(out, expected); let decoded = decode(&expected).expect("decode failure"); assert_eq!(foo, decoded); } #[test] fn test_encode_foo_wrapper() { let foo = FooWrapper { a: "cat".into() }; let expected = vec![0x83, b'c', b'a', b't']; let out = encode(&foo); assert_eq!(out, expected); 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>, } 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)); } rlp-derive-0.1.0/.cargo_vcs_info.json0000644000000001121362124440700131760ustar00{ "git": { "sha1": "e5dd7d46155664f49b46ab865ac3427d27232d56" } }