tetsy_scale_codec_derive/
lib.rs1#![recursion_limit = "128"]
18extern crate proc_macro;
19
20#[macro_use]
21extern crate syn;
22
23#[macro_use]
24extern crate quote;
25
26use proc_macro2::{Ident, Span};
27use proc_macro_crate::crate_name;
28use syn::spanned::Spanned;
29use syn::{Data, Field, Fields, DeriveInput, Error};
30
31mod decode;
32mod encode;
33mod utils;
34mod trait_bounds;
35
36fn include_tetsy_scale_codec_crate() -> proc_macro2::TokenStream {
38 if std::env::var("CARGO_PKG_NAME").unwrap() == "tetsy-scale-codec" {
40 quote!( extern crate tetsy_scale_codec as _tetsy_scale_codec; )
41 } else {
42 match crate_name("tetsy-scale-codec") {
43 Ok(tetsy_codec_crate) => {
44 let ident = Ident::new(&tetsy_codec_crate, Span::call_site());
45 quote!( extern crate #ident as _tetsy_scale_codec; )
46 },
47 Err(e) => Error::new(Span::call_site(), &e).to_compile_error(),
48 }
49 }
50}
51
52fn wrap_with_dummy_const(impl_block: proc_macro2::TokenStream) -> proc_macro::TokenStream {
54 let tetsy_codec_crate = include_tetsy_scale_codec_crate();
55
56 let generated = quote! {
57 const _: () = {
58 #[allow(unknown_lints)]
59 #[cfg_attr(feature = "cargo-clippy", allow(useless_attribute))]
60 #[allow(rust_2018_idioms)]
61 #tetsy_codec_crate
62 #impl_block
63 };
64 };
65
66 generated.into()
67}
68
69#[proc_macro_derive(Encode, attributes(codec))]
133pub fn encode_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
134 let mut input: DeriveInput = match syn::parse(input) {
135 Ok(input) => input,
136 Err(e) => return e.to_compile_error().into(),
137 };
138
139 if let Err(e) = utils::check_attributes(&input) {
140 return e.to_compile_error().into();
141 }
142
143 if let Err(e) = trait_bounds::add(
144 &input.ident,
145 &mut input.generics,
146 &input.data,
147 parse_quote!(_tetsy_scale_codec::Encode),
148 None,
149 utils::has_dumb_trait_bound(&input.attrs),
150 ) {
151 return e.to_compile_error().into();
152 }
153
154 let name = &input.ident;
155 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
156
157 let encode_impl = encode::quote(&input.data, name);
158
159 let impl_block = quote! {
160 impl #impl_generics _tetsy_scale_codec::Encode for #name #ty_generics #where_clause {
161 #encode_impl
162 }
163
164 impl #impl_generics _tetsy_scale_codec::EncodeLike for #name #ty_generics #where_clause {}
165 };
166
167 wrap_with_dummy_const(impl_block)
168}
169
170#[proc_macro_derive(Decode, attributes(codec))]
174pub fn decode_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
175 let mut input: DeriveInput = match syn::parse(input) {
176 Ok(input) => input,
177 Err(e) => return e.to_compile_error().into(),
178 };
179
180 if let Err(e) = utils::check_attributes(&input) {
181 return e.to_compile_error().into();
182 }
183
184 if let Err(e) = trait_bounds::add(
185 &input.ident,
186 &mut input.generics,
187 &input.data,
188 parse_quote!(_tetsy_scale_codec::Decode),
189 Some(parse_quote!(Default)),
190 utils::has_dumb_trait_bound(&input.attrs),
191 ) {
192 return e.to_compile_error().into();
193 }
194
195 let name = &input.ident;
196 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
197
198 let input_ = quote!(__codec_input_edqy);
199 let decoding = decode::quote(&input.data, name, &input_);
200
201 let impl_block = quote! {
202 impl #impl_generics _tetsy_scale_codec::Decode for #name #ty_generics #where_clause {
203 fn decode<__CodecInputEdqy: _tetsy_scale_codec::Input>(
204 #input_: &mut __CodecInputEdqy
205 ) -> core::result::Result<Self, _tetsy_scale_codec::Error> {
206 #decoding
207 }
208 }
209 };
210
211 wrap_with_dummy_const(impl_block)
212}
213
214#[proc_macro_derive(CompactAs, attributes(codec))]
229pub fn compact_as_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
230 let mut input: DeriveInput = match syn::parse(input) {
231 Ok(input) => input,
232 Err(e) => return e.to_compile_error().into(),
233 };
234
235 if let Err(e) = utils::check_attributes(&input) {
236 return e.to_compile_error().into();
237 }
238
239 if let Err(e) = trait_bounds::add(
240 &input.ident,
241 &mut input.generics,
242 &input.data,
243 parse_quote!(_tetsy_scale_codec::CompactAs),
244 None,
245 utils::has_dumb_trait_bound(&input.attrs),
246 ) {
247 return e.to_compile_error().into();
248 }
249
250 let name = &input.ident;
251 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
252
253 fn val_or_default(field: &Field) -> proc_macro2::TokenStream {
254 let skip = utils::should_skip(&field.attrs);
255 if skip {
256 quote_spanned!(field.span()=> Default::default())
257 } else {
258 quote_spanned!(field.span()=> x)
259 }
260 }
261
262 let (inner_ty, inner_field, constructor) = match input.data {
263 Data::Struct(ref data) => {
264 match data.fields {
265 Fields::Named(ref fields) if utils::filter_skip_named(fields).count() == 1 => {
266 let recurse = fields.named.iter().map(|f| {
267 let name_ident = &f.ident;
268 let val_or_default = val_or_default(&f);
269 quote_spanned!(f.span()=> #name_ident: #val_or_default)
270 });
271 let field = utils::filter_skip_named(fields).next().expect("Exactly one field");
272 let field_name = &field.ident;
273 let constructor = quote!( #name { #( #recurse, )* });
274 (&field.ty, quote!(&self.#field_name), constructor)
275 },
276 Fields::Unnamed(ref fields) if utils::filter_skip_unnamed(fields).count() == 1 => {
277 let recurse = fields.unnamed.iter().enumerate().map(|(_, f) | {
278 let val_or_default = val_or_default(&f);
279 quote_spanned!(f.span()=> #val_or_default)
280 });
281 let (id, field) = utils::filter_skip_unnamed(fields).next().expect("Exactly one field");
282 let id = syn::Index::from(id);
283 let constructor = quote!( #name(#( #recurse, )*));
284 (&field.ty, quote!(&self.#id), constructor)
285 },
286 _ => {
287 return Error::new(
288 data.fields.span(),
289 "Only structs with a single non-skipped field can derive CompactAs"
290 ).to_compile_error().into();
291 },
292 }
293 },
294 Data::Enum(syn::DataEnum { enum_token: syn::token::Enum { span }, .. }) |
295 Data::Union(syn::DataUnion { union_token: syn::token::Union { span }, .. }) => {
296 return Error::new(span, "Only structs can derive CompactAs").to_compile_error().into();
297 },
298 };
299
300 let impl_block = quote! {
301 impl #impl_generics _tetsy_scale_codec::CompactAs for #name #ty_generics #where_clause {
302 type As = #inner_ty;
303 fn encode_as(&self) -> &#inner_ty {
304 #inner_field
305 }
306 fn decode_from(x: #inner_ty)
307 -> core::result::Result<#name #ty_generics, _tetsy_scale_codec::Error>
308 {
309 Ok(#constructor)
310 }
311 }
312
313 impl #impl_generics From<_tetsy_scale_codec::Compact<#name #ty_generics>>
314 for #name #ty_generics #where_clause
315 {
316 fn from(x: _tetsy_scale_codec::Compact<#name #ty_generics>) -> #name #ty_generics {
317 x.0
318 }
319 }
320 };
321
322 wrap_with_dummy_const(impl_block)
323}