referrerpolicy=no-referrer-when-downgrade

frame_support_procedural_tools_derive/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18// tag::description[]
19//! Use to derive parsing for parsing struct.
20// end::description[]
21
22use proc_macro::TokenStream;
23use proc_macro2::Span;
24use quote::quote;
25use syn::parse_macro_input;
26
27pub(crate) fn fields_idents(
28	fields: impl Iterator<Item = syn::Field>,
29) -> impl Iterator<Item = proc_macro2::TokenStream> {
30	fields.enumerate().map(|(ix, field)| {
31		field.ident.map(|i| quote! {#i}).unwrap_or_else(|| {
32			let f_ix: syn::Ident = syn::Ident::new(&format!("f_{}", ix), Span::call_site());
33			quote!( #f_ix )
34		})
35	})
36}
37
38pub(crate) fn fields_access(
39	fields: impl Iterator<Item = syn::Field>,
40) -> impl Iterator<Item = proc_macro2::TokenStream> {
41	fields.enumerate().map(|(ix, field)| {
42		field.ident.map(|i| quote!( #i )).unwrap_or_else(|| {
43			let f_ix: syn::Index = syn::Index { index: ix as u32, span: Span::call_site() };
44			quote!( #f_ix )
45		})
46	})
47}
48
49/// self defined parsing struct.
50/// not meant for any struct, just for fast
51/// parse implementation.
52#[proc_macro_derive(Parse)]
53pub fn derive_parse(input: TokenStream) -> TokenStream {
54	let item = parse_macro_input!(input as syn::Item);
55	match item {
56		syn::Item::Struct(input) => derive_parse_struct(input),
57		_ => TokenStream::new(), // ignore
58	}
59}
60
61fn derive_parse_struct(input: syn::ItemStruct) -> TokenStream {
62	let syn::ItemStruct { ident, generics, fields, .. } = input;
63	let field_names = {
64		let name = fields_idents(fields.iter().map(Clone::clone));
65		quote! {
66			#(
67				#name,
68			)*
69		}
70	};
71	let field = fields_idents(fields.iter().map(Clone::clone));
72	let tokens = quote! {
73		impl #generics syn::parse::Parse for #ident #generics {
74			fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
75				#(
76					let #field = input.parse()?;
77				)*
78				Ok(Self {
79					#field_names
80				})
81			}
82		}
83	};
84	tokens.into()
85}
86
87/// self defined parsing struct or enum.
88/// not meant for any struct/enum, just for fast
89/// parse implementation.
90/// For enum:
91///   it only output fields (empty field act as a None).
92#[proc_macro_derive(ToTokens)]
93pub fn derive_totokens(input: TokenStream) -> TokenStream {
94	let item = parse_macro_input!(input as syn::Item);
95	match item {
96		syn::Item::Enum(input) => derive_totokens_enum(input),
97		syn::Item::Struct(input) => derive_totokens_struct(input),
98		_ => TokenStream::new(), // ignore
99	}
100}
101
102fn derive_totokens_struct(input: syn::ItemStruct) -> TokenStream {
103	let syn::ItemStruct { ident, generics, fields, .. } = input;
104
105	let fields = fields_access(fields.iter().map(Clone::clone));
106	let tokens = quote! {
107
108		impl #generics quote::ToTokens for #ident #generics {
109			fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
110				#(
111					self.#fields.to_tokens(tokens);
112				)*
113			}
114		}
115
116	};
117	tokens.into()
118}
119
120fn derive_totokens_enum(input: syn::ItemEnum) -> TokenStream {
121	let syn::ItemEnum { ident, generics, variants, .. } = input;
122	let variants = variants.iter().map(|v| {
123		let v_ident = v.ident.clone();
124		let fields_build = if v.fields.iter().count() > 0 {
125			let fields_id = fields_idents(v.fields.iter().map(Clone::clone));
126			quote!( (#(#fields_id), *) )
127		} else {
128			quote!()
129		};
130		let field = fields_idents(v.fields.iter().map(Clone::clone));
131		quote! {
132			#ident::#v_ident #fields_build => {
133				#(
134					#field.to_tokens(tokens);
135				)*
136			},
137		}
138	});
139	let tokens = quote! {
140		impl #generics quote::ToTokens for #ident #generics {
141			fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
142				match self {
143					#(
144						#variants
145					)*
146				}
147			}
148		}
149	};
150
151	tokens.into()
152}