// This file is part of Substrate. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // tag::description[] //! Use to derive parsing for parsing struct. // end::description[] use proc_macro::TokenStream; use proc_macro2::Span; use quote::quote; use syn::parse_macro_input; pub(crate) fn fields_idents( fields: impl Iterator, ) -> impl Iterator { fields.enumerate().map(|(ix, field)| { field.ident.map(|i| quote! {#i}).unwrap_or_else(|| { let f_ix: syn::Ident = syn::Ident::new(&format!("f_{}", ix), Span::call_site()); quote!( #f_ix ) }) }) } pub(crate) fn fields_access( fields: impl Iterator, ) -> impl Iterator { fields.enumerate().map(|(ix, field)| { field.ident.map(|i| quote!( #i )).unwrap_or_else(|| { let f_ix: syn::Index = syn::Index { index: ix as u32, span: Span::call_site() }; quote!( #f_ix ) }) }) } /// self defined parsing struct. /// not meant for any struct, just for fast /// parse implementation. #[proc_macro_derive(Parse)] pub fn derive_parse(input: TokenStream) -> TokenStream { let item = parse_macro_input!(input as syn::Item); match item { syn::Item::Struct(input) => derive_parse_struct(input), _ => TokenStream::new(), // ignore } } fn derive_parse_struct(input: syn::ItemStruct) -> TokenStream { let syn::ItemStruct { ident, generics, fields, .. } = input; let field_names = { let name = fields_idents(fields.iter().map(Clone::clone)); quote! { #( #name, )* } }; let field = fields_idents(fields.iter().map(Clone::clone)); let tokens = quote! { impl #generics syn::parse::Parse for #ident #generics { fn parse(input: syn::parse::ParseStream) -> syn::parse::Result { #( let #field = input.parse()?; )* Ok(Self { #field_names }) } } }; tokens.into() } /// self defined parsing struct or enum. /// not meant for any struct/enum, just for fast /// parse implementation. /// For enum: /// it only output fields (empty field act as a None). #[proc_macro_derive(ToTokens)] pub fn derive_totokens(input: TokenStream) -> TokenStream { let item = parse_macro_input!(input as syn::Item); match item { syn::Item::Enum(input) => derive_totokens_enum(input), syn::Item::Struct(input) => derive_totokens_struct(input), _ => TokenStream::new(), // ignore } } fn derive_totokens_struct(input: syn::ItemStruct) -> TokenStream { let syn::ItemStruct { ident, generics, fields, .. } = input; let fields = fields_access(fields.iter().map(Clone::clone)); let tokens = quote! { impl #generics quote::ToTokens for #ident #generics { fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { #( self.#fields.to_tokens(tokens); )* } } }; tokens.into() } fn derive_totokens_enum(input: syn::ItemEnum) -> TokenStream { let syn::ItemEnum { ident, generics, variants, .. } = input; let variants = variants.iter().map(|v| { let v_ident = v.ident.clone(); let fields_build = if v.fields.iter().count() > 0 { let fields_id = fields_idents(v.fields.iter().map(Clone::clone)); quote!( (#(#fields_id), *) ) } else { quote!() }; let field = fields_idents(v.fields.iter().map(Clone::clone)); quote! { #ident::#v_ident #fields_build => { #( #field.to_tokens(tokens); )* }, } }); let tokens = quote! { impl #generics quote::ToTokens for #ident #generics { fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { match self { #( #variants )* } } } }; tokens.into() }