referrerpolicy=no-referrer-when-downgrade

sp_debug_derive/
impls.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
18use proc_macro2::TokenStream;
19use quote::quote;
20use syn::{parse_quote, Data, DeriveInput};
21
22pub fn debug_derive(ast: DeriveInput) -> proc_macro::TokenStream {
23	let name_str = ast.ident.to_string();
24	let implementation = implementation::derive(&name_str, &ast.data);
25	let name = &ast.ident;
26	let mut generics = ast.generics.clone();
27	let (impl_generics, ty_generics, where_clause) = {
28		let wh = generics.make_where_clause();
29		for t in ast.generics.type_params() {
30			let name = &t.ident;
31			wh.predicates.push(parse_quote! { #name : core::fmt::Debug });
32		}
33		generics.split_for_impl()
34	};
35	let gen = quote! {
36		impl #impl_generics core::fmt::Debug for #name #ty_generics #where_clause {
37			fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
38				#implementation
39			}
40		}
41	};
42
43	gen.into()
44}
45
46#[cfg(all(not(feature = "std"), not(feature = "force-debug")))]
47mod implementation {
48	use super::*;
49
50	/// Derive the inner implementation of `Debug::fmt` function.
51	///
52	/// Non-std environment. We do nothing to prevent bloating the size of runtime.
53	/// Implement `Printable` if you need to print the details.
54	pub fn derive(_name_str: &str, _data: &Data) -> TokenStream {
55		quote! {
56			fmt.write_str("<wasm:stripped>")
57		}
58	}
59}
60
61#[cfg(any(feature = "std", feature = "force-debug"))]
62mod implementation {
63	use super::*;
64	use proc_macro2::Span;
65	use syn::{token::SelfValue, Ident, Index};
66
67	/// Derive the inner implementation of `Debug::fmt` function.
68	pub fn derive(name_str: &str, data: &Data) -> TokenStream {
69		match *data {
70			Data::Struct(ref s) => derive_struct(name_str, &s.fields),
71			Data::Union(ref u) => derive_fields(name_str, Fields::new(u.fields.named.iter(), None)),
72			Data::Enum(ref e) => derive_enum(name_str, e),
73		}
74	}
75
76	enum Fields {
77		Indexed { indices: Vec<Index> },
78		Unnamed { vars: Vec<Ident> },
79		Named { names: Vec<Ident>, this: Option<SelfValue> },
80	}
81
82	impl Fields {
83		fn new<'a>(fields: impl Iterator<Item = &'a syn::Field>, this: Option<SelfValue>) -> Self {
84			let mut indices = vec![];
85			let mut names = vec![];
86
87			for (i, f) in fields.enumerate() {
88				if let Some(ident) = f.ident.clone() {
89					names.push(ident);
90				} else {
91					indices.push(Index::from(i));
92				}
93			}
94
95			if names.is_empty() {
96				Self::Indexed { indices }
97			} else {
98				Self::Named { names, this }
99			}
100		}
101	}
102
103	fn derive_fields(name_str: &str, fields: Fields) -> TokenStream {
104		match fields {
105			Fields::Named { names, this } => {
106				let names_str: Vec<_> = names.iter().map(|x| x.to_string()).collect();
107
108				let fields = match this {
109					None => quote! { #( .field(#names_str, #names) )* },
110					Some(this) => quote! { #( .field(#names_str, &#this.#names) )* },
111				};
112
113				quote! {
114					fmt.debug_struct(#name_str)
115						#fields
116						.finish()
117				}
118			},
119			Fields::Indexed { indices } => {
120				quote! {
121					fmt.debug_tuple(#name_str)
122						#( .field(&self.#indices) )*
123						.finish()
124				}
125			},
126			Fields::Unnamed { vars } => {
127				quote! {
128					fmt.debug_tuple(#name_str)
129						#( .field(#vars) )*
130						.finish()
131				}
132			},
133		}
134	}
135
136	fn derive_enum(name: &str, e: &syn::DataEnum) -> TokenStream {
137		let v = e.variants.iter().map(|v| {
138			let name = format!("{}::{}", name, v.ident);
139			let ident = &v.ident;
140			match v.fields {
141				syn::Fields::Named(ref f) => {
142					let names: Vec<_> = f.named.iter().flat_map(|f| f.ident.clone()).collect();
143					let fields_impl =
144						derive_fields(&name, Fields::Named { names: names.clone(), this: None });
145					(ident, (quote! { { #( ref #names ),* } }, fields_impl))
146				},
147				syn::Fields::Unnamed(ref f) => {
148					let names = f
149						.unnamed
150						.iter()
151						.enumerate()
152						.map(|(id, _)| Ident::new(&format!("a{}", id), Span::call_site()))
153						.collect::<Vec<_>>();
154					let fields_impl = derive_fields(&name, Fields::Unnamed { vars: names.clone() });
155					(ident, (quote! { ( #( ref #names ),* ) }, fields_impl))
156				},
157				syn::Fields::Unit => {
158					let fields_impl = derive_fields(&name, Fields::Indexed { indices: vec![] });
159					(ident, (quote! {}, fields_impl))
160				},
161			}
162		});
163
164		type Vecs<A, B> = (Vec<A>, Vec<B>);
165		let (variants, others): Vecs<_, _> = v.unzip();
166		let (match_fields, variants_impl): Vecs<_, _> = others.into_iter().unzip();
167
168		quote! {
169			match self {
170				#( Self::#variants #match_fields => #variants_impl, )*
171				_ => Ok(()),
172			}
173		}
174	}
175
176	fn derive_struct(name_str: &str, fields: &syn::Fields) -> TokenStream {
177		match *fields {
178			syn::Fields::Named(ref f) => derive_fields(
179				name_str,
180				Fields::new(f.named.iter(), Some(syn::Token!(self)(Span::call_site()))),
181			),
182			syn::Fields::Unnamed(ref f) =>
183				derive_fields(name_str, Fields::new(f.unnamed.iter(), None)),
184			syn::Fields::Unit => derive_fields(name_str, Fields::Indexed { indices: vec![] }),
185		}
186	}
187}