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
106
107
108
109
110
111
112
113
114
115
// Copyright 2021 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.

// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.

use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote};

pub fn generate_conversion_functions(input: proc_macro::TokenStream) -> syn::Result<TokenStream> {
	if !input.is_empty() {
		return Err(syn::Error::new(Span::call_site(), "No arguments expected"))
	}

	let from_tuples = generate_conversion_from_tuples();
	let from_v1 = generate_conversion_from_v1();

	Ok(quote! {
		#from_tuples
		#from_v1
	})
}

fn generate_conversion_from_tuples() -> TokenStream {
	let from_tuples = (0..8usize)
		.map(|num_junctions| {
			let junctions =
				(0..=num_junctions).map(|_| format_ident!("Junction")).collect::<Vec<_>>();
			let idents = (0..=num_junctions).map(|i| format_ident!("j{}", i)).collect::<Vec<_>>();
			let variant = &format_ident!("X{}", num_junctions + 1);
			let array_size = num_junctions + 1;

			quote! {
				impl From<( #(#junctions,)* )> for MultiLocation {
					fn from( ( #(#idents,)* ): ( #(#junctions,)* ) ) -> Self {
						MultiLocation::#variant( #(#idents),* )
					}
				}

				impl From<[Junction; #array_size]> for MultiLocation {
					fn from(j: [Junction; #array_size]) -> Self {
						let [#(#idents),*] = j;
						MultiLocation::#variant( #(#idents),* )
					}
				}
			}
		})
		.collect::<TokenStream>();

	quote! {
		impl From<()> for MultiLocation {
			fn from(_: ()) -> Self {
				MultiLocation::Null
			}
		}

		impl From<Junction> for MultiLocation {
			fn from(x: Junction) -> Self {
				MultiLocation::X1(x)
			}
		}

		impl From<[Junction; 0]> for MultiLocation {
			fn from(_: [Junction; 0]) -> Self {
				MultiLocation::Null
			}
		}

		#from_tuples
	}
}

fn generate_conversion_from_v1() -> TokenStream {
	let match_variants = (0..8u8)
		.map(|cur_num| {
			let variant = format_ident!("X{}", cur_num + 1);
			let idents = (1..=cur_num).map(|i| format_ident!("j{}", i)).collect::<Vec<_>>();

			quote! {
				crate::v1::Junctions::#variant( j0 #(, #idents)* ) => res
					.pushed_with(Junction::from(j0))
					#( .and_then(|res| res.pushed_with(Junction::from(#idents))) )*
					.map_err(|_| ()),
			}
		})
		.collect::<TokenStream>();

	quote! {
		impl TryFrom<crate::v1::MultiLocation> for MultiLocation {
			type Error = ();
			fn try_from(v1: crate::v1::MultiLocation) -> core::result::Result<Self, ()> {
				let mut res = MultiLocation::Null;

				for _ in 0..v1.parents {
					res.push(Junction::Parent)?;
				}

				match v1.interior {
					crate::v1::Junctions::Here => Ok(res),
					#match_variants
				}
			}
		}
	}
}