parity_codec_derive/
lib.rs

1// Copyright 2017-2018 Parity Technologies
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Derives serialization and deserialization codec for complex structs for simple marshalling.
16
17extern crate proc_macro;
18use proc_macro2;
19
20#[macro_use]
21extern crate syn;
22
23#[macro_use]
24extern crate quote;
25
26use proc_macro::TokenStream;
27use proc_macro2::Span;
28use syn::{DeriveInput, Ident, parse::Error};
29use proc_macro_crate::crate_name;
30
31use std::env;
32
33mod decode;
34mod encode;
35mod utils;
36mod trait_bounds;
37
38/// Include the `parity-codec` crate under a known name (`_parity_codec`).
39fn include_parity_codec_crate() -> proc_macro2::TokenStream {
40	// This "hack" is required for the tests.
41	if env::var("CARGO_PKG_NAME").unwrap() == "parity-codec" {
42		quote!( extern crate parity_codec as _parity_codec; )
43	} else {
44		match crate_name("parity-codec") {
45			Ok(parity_codec_crate) => {
46				let ident = Ident::new(&parity_codec_crate, Span::call_site());
47				quote!( extern crate #ident as _parity_codec; )
48			},
49			Err(e) => Error::new(Span::call_site(), &e).to_compile_error(),
50		}
51	}
52}
53
54#[proc_macro_derive(Encode, attributes(codec))]
55pub fn encode_derive(input: TokenStream) -> TokenStream {
56	let mut input: DeriveInput = match syn::parse(input) {
57		Ok(input) => input,
58		Err(e) => return e.to_compile_error().into(),
59	};
60	if let Some(span) = utils::get_skip(&input.attrs) {
61		return Error::new(span, "invalid attribute `skip` on root input")
62			.to_compile_error().into();
63	}
64
65	if let Err(e) = trait_bounds::add(
66		&input.ident,
67		&mut input.generics,
68		&input.data,
69		parse_quote!(_parity_codec::Encode),
70		None,
71	) {
72		return e.to_compile_error().into();
73	}
74
75	let name = &input.ident;
76	let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
77
78	let self_ = quote!(self);
79	let dest_ = quote!(dest);
80	let encoding = encode::quote(&input.data, name, &self_, &dest_);
81
82	let impl_block = quote! {
83		impl #impl_generics _parity_codec::Encode for #name #ty_generics #where_clause {
84			fn encode_to<EncOut: _parity_codec::Output>(&#self_, #dest_: &mut EncOut) {
85				#encoding
86			}
87		}
88	};
89
90	let mut new_name = "_IMPL_ENCODE_FOR_".to_string();
91	new_name.push_str(name.to_string().trim_start_matches("r#"));
92	let dummy_const = Ident::new(&new_name, Span::call_site());
93	let parity_codec_crate = include_parity_codec_crate();
94
95	let generated = quote! {
96		#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
97		const #dummy_const: () = {
98			#[allow(unknown_lints)]
99			#[cfg_attr(feature = "cargo-clippy", allow(useless_attribute))]
100			#[allow(rust_2018_idioms)]
101			#parity_codec_crate
102			#impl_block
103		};
104	};
105
106	generated.into()
107}
108
109#[proc_macro_derive(Decode, attributes(codec))]
110pub fn decode_derive(input: TokenStream) -> TokenStream {
111	let mut input: DeriveInput = match syn::parse(input) {
112		Ok(input) => input,
113		Err(e) => return e.to_compile_error().into(),
114	};
115	if let Some(span) = utils::get_skip(&input.attrs) {
116		return Error::new(span, "invalid attribute `skip` on root input")
117			.to_compile_error().into();
118	}
119
120	if let Err(e) = trait_bounds::add(
121		&input.ident,
122		&mut input.generics,
123		&input.data,
124		parse_quote!(_parity_codec::Decode),
125		Some(parse_quote!(Default))
126	) {
127		return e.to_compile_error().into();
128	}
129
130	let name = &input.ident;
131	let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
132
133	let input_ = quote!(input);
134	let decoding = decode::quote(&input.data, name, &input_);
135
136	let impl_block = quote! {
137		impl #impl_generics _parity_codec::Decode for #name #ty_generics #where_clause {
138			fn decode<DecIn: _parity_codec::Input>(#input_: &mut DecIn) -> Option<Self> {
139				#decoding
140			}
141		}
142	};
143
144	let mut new_name = "_IMPL_DECODE_FOR_".to_string();
145	new_name.push_str(name.to_string().trim_start_matches("r#"));
146	let dummy_const = Ident::new(&new_name, Span::call_site());
147	let parity_codec_crate = include_parity_codec_crate();
148
149	let generated = quote! {
150		#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
151		const #dummy_const: () = {
152			#[allow(unknown_lints)]
153			#[cfg_attr(feature = "cargo-clippy", allow(useless_attribute))]
154			#[allow(rust_2018_idioms)]
155			#parity_codec_crate
156			#impl_block
157		};
158	};
159
160	generated.into()
161}