1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// 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.

//! Implementation of the `create_tt_return_macro` macro

use crate::COUNTER;
use proc_macro2::{Ident, TokenStream};
use quote::format_ident;

struct CreateTtReturnMacroDef {
	name: Ident,
	args: Vec<(Ident, TokenStream)>,
}

impl syn::parse::Parse for CreateTtReturnMacroDef {
	fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
		let name = input.parse()?;
		let _ = input.parse::<syn::Token![,]>()?;

		let mut args = Vec::new();
		while !input.is_empty() {
			let mut value;
			let key: Ident = input.parse()?;
			let _ = input.parse::<syn::Token![=]>()?;
			let _: syn::token::Bracket = syn::bracketed!(value in input);
			let _: syn::token::Brace = syn::braced!(value in value);
			let value: TokenStream = value.parse()?;

			args.push((key, value))
		}

		Ok(Self { name, args })
	}
}

/// A proc macro that accepts a name and any number of key-value pairs, to be used to create a
/// declarative macro that follows tt-call conventions and simply calls
/// [`tt_call::tt_return`], accepting an optional `frame-support` argument and returning
/// the key-value pairs that were supplied to the proc macro.
///
/// # Example
/// ```ignore
/// __create_tt_macro! {
///     my_tt_macro,
///     foo = [{ bar }]
/// }
///
/// // Creates the following declarative macro:
///
/// macro_rules! my_tt_macro {
///     {
///         $caller:tt
///         $(your_tt_return = [{ $my_tt_return:path }])?
///     } => {
///         $my_tt_return! {
///             $caller
///             foo = [{ bar }]
///         }
///     }
/// }
/// ```
pub fn create_tt_return_macro(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
	let CreateTtReturnMacroDef { name, args } =
		syn::parse_macro_input!(input as CreateTtReturnMacroDef);

	let (keys, values): (Vec<_>, Vec<_>) = args.into_iter().unzip();
	let count = COUNTER.with(|counter| counter.borrow_mut().inc());
	let unique_name = format_ident!("{}_{}", name, count);

	let decl_macro = quote::quote! {
		#[macro_export]
		#[doc(hidden)]
		macro_rules! #unique_name {
			{
				$caller:tt
				$(your_tt_return = [{ $my_tt_macro:path }])?
			} => {
				$my_tt_return! {
					$caller
					#(
						#keys = [{ #values }]
					)*
				}
			}
		}

		pub use #unique_name as #name;
	};

	decl_macro.into()
}