mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 08:11:03 +00:00
Use proc macros to generate conversion functions for MultiLocation (#3635)
* Use proc macros to generate conversion functions for MultiLocation * Add compile test and missing conversion cases * Add common derives for Parent and Ancestor * Generate conversion functions for MultiLocation v0 via proc macro * Add type conversion test and fix a bug * cargo fmt * Do not hardcode 8 as the number of max parents * Use map instead of for loops when generating code fragments * Spelling * cargo fmt * More mapping, less for-looping
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
name = "xcm-procedural"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0.28"
|
||||
quote = "1.0.9"
|
||||
syn = "1.0.74"
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright 2020 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/>.
|
||||
|
||||
//! Procedural macros used in XCM.
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
mod v0;
|
||||
mod v1;
|
||||
|
||||
#[proc_macro]
|
||||
pub fn impl_conversion_functions_for_multilocation_v0(input: TokenStream) -> TokenStream {
|
||||
v0::multilocation::generate_conversion_functions(input)
|
||||
.unwrap_or_else(syn::Error::into_compile_error)
|
||||
.into()
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn impl_conversion_functions_for_multilocation_v1(input: TokenStream) -> TokenStream {
|
||||
v1::multilocation::generate_conversion_functions(input)
|
||||
.unwrap_or_else(syn::Error::into_compile_error)
|
||||
.into()
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// 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/>.
|
||||
|
||||
pub mod multilocation;
|
||||
@@ -0,0 +1,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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// 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/>.
|
||||
|
||||
pub mod multilocation;
|
||||
@@ -0,0 +1,204 @@
|
||||
// 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};
|
||||
use syn::{Result, Token};
|
||||
|
||||
pub fn generate_conversion_functions(input: proc_macro::TokenStream) -> Result<TokenStream> {
|
||||
if !input.is_empty() {
|
||||
return Err(syn::Error::new(Span::call_site(), "No arguments expected"))
|
||||
}
|
||||
|
||||
// Support up to 8 Parents in a tuple, assuming that most use cases don't go past 8 parents.
|
||||
let from_tuples = generate_conversion_from_tuples(8);
|
||||
let from_v0 = generate_conversion_from_v0();
|
||||
|
||||
Ok(quote! {
|
||||
#from_tuples
|
||||
#from_v0
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_conversion_from_tuples(max_parents: u8) -> TokenStream {
|
||||
let mut 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;
|
||||
|
||||
let mut from_tuple = quote! {
|
||||
impl From<( #(#junctions,)* )> for MultiLocation {
|
||||
fn from( ( #(#idents,)* ): ( #(#junctions,)* ) ) -> Self {
|
||||
MultiLocation { parents: 0, interior: Junctions::#variant( #(#idents),* ) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(u8, #(#junctions),*)> for MultiLocation {
|
||||
fn from( ( parents, #(#idents),* ): (u8, #(#junctions),* ) ) -> Self {
|
||||
MultiLocation { parents, interior: Junctions::#variant( #(#idents),* ) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(Ancestor, #(#junctions),*)> for MultiLocation {
|
||||
fn from( ( Ancestor(parents), #(#idents),* ): (Ancestor, #(#junctions),* ) ) -> Self {
|
||||
MultiLocation { parents, interior: Junctions::#variant( #(#idents),* ) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[Junction; #array_size]> for MultiLocation {
|
||||
fn from(j: [Junction; #array_size]) -> Self {
|
||||
let [#(#idents),*] = j;
|
||||
MultiLocation { parents: 0, interior: Junctions::#variant( #(#idents),* ) }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let from_parent_tuples = (1..=max_parents).map(|cur_parents| {
|
||||
let parents = (0..cur_parents).map(|_| format_ident!("Parent")).collect::<Vec<_>>();
|
||||
let underscores =
|
||||
(0..cur_parents).map(|_| Token)).collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
impl From<( #(#parents,)* #(#junctions),* )> for MultiLocation {
|
||||
fn from( (#(#underscores,)* #(#idents),*): ( #(#parents,)* #(#junctions),* ) ) -> Self {
|
||||
MultiLocation { parents: #cur_parents, interior: Junctions::#variant( #(#idents),* ) }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
from_tuple.extend(from_parent_tuples);
|
||||
from_tuple
|
||||
})
|
||||
.collect::<TokenStream>();
|
||||
|
||||
let from_parent_junctions_tuples = (1..=max_parents).map(|cur_parents| {
|
||||
let parents = (0..cur_parents).map(|_| format_ident!("Parent")).collect::<Vec<_>>();
|
||||
let underscores =
|
||||
(0..cur_parents).map(|_| Token)).collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
impl From<( #(#parents,)* Junctions )> for MultiLocation {
|
||||
fn from( (#(#underscores,)* junctions): ( #(#parents,)* Junctions ) ) -> Self {
|
||||
MultiLocation { parents: #cur_parents, interior: junctions }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
from_tuples.extend(from_parent_junctions_tuples);
|
||||
|
||||
quote! {
|
||||
impl From<Junctions> for MultiLocation {
|
||||
fn from(junctions: Junctions) -> Self {
|
||||
MultiLocation { parents: 0, interior: junctions }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(u8, Junctions)> for MultiLocation {
|
||||
fn from((parents, interior): (u8, Junctions)) -> Self {
|
||||
MultiLocation { parents, interior }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(Ancestor, Junctions)> for MultiLocation {
|
||||
fn from((Ancestor(parents), interior): (Ancestor, Junctions)) -> Self {
|
||||
MultiLocation { parents, interior }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<()> for MultiLocation {
|
||||
fn from(_: ()) -> Self {
|
||||
MultiLocation { parents: 0, interior: Junctions::Here }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(u8,)> for MultiLocation {
|
||||
fn from((parents,): (u8,)) -> Self {
|
||||
MultiLocation { parents, interior: Junctions::Here }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Junction> for MultiLocation {
|
||||
fn from(x: Junction) -> Self {
|
||||
MultiLocation { parents: 0, interior: Junctions::X1(x) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[Junction; 0]> for MultiLocation {
|
||||
fn from(_: [Junction; 0]) -> Self {
|
||||
MultiLocation { parents: 0, interior: Junctions::Here }
|
||||
}
|
||||
}
|
||||
|
||||
#from_tuples
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_conversion_from_v0() -> TokenStream {
|
||||
let match_variants = (0..8u8)
|
||||
.map(|cur_num| {
|
||||
let num_ancestors = cur_num + 1;
|
||||
let variant = format_ident!("X{}", num_ancestors);
|
||||
let idents = (0..=cur_num).map(|i| format_ident!("j{}", i)).collect::<Vec<_>>();
|
||||
|
||||
let intermediate_match_arms = (1..num_ancestors)
|
||||
.rev()
|
||||
.map(|parent_count| {
|
||||
let parent_idents =
|
||||
(0..parent_count).map(|j| format_ident!("j{}", j)).collect::<Vec<_>>();
|
||||
let junction_idents = (parent_count..num_ancestors)
|
||||
.map(|j| format_ident!("j{}", j))
|
||||
.collect::<Vec<_>>();
|
||||
let junction_variant = format_ident!("X{}", num_ancestors - parent_count);
|
||||
|
||||
quote! {
|
||||
crate::v0::MultiLocation::#variant( #(#idents),* )
|
||||
if #( #parent_idents.is_parent() )&&* =>
|
||||
Ok(MultiLocation {
|
||||
parents: #parent_count,
|
||||
interior: #junction_variant( #( #junction_idents.try_into()? ),* ),
|
||||
}),
|
||||
}
|
||||
})
|
||||
.collect::<TokenStream>();
|
||||
|
||||
quote! {
|
||||
crate::v0::MultiLocation::#variant( #(#idents),* )
|
||||
if #( #idents.is_parent() )&&* =>
|
||||
Ok(MultiLocation::ancestor(#num_ancestors)),
|
||||
#intermediate_match_arms
|
||||
crate::v0::MultiLocation::#variant( #(#idents),* ) =>
|
||||
Ok( #variant( #( #idents.try_into()? ),* ).into() ),
|
||||
}
|
||||
})
|
||||
.collect::<TokenStream>();
|
||||
|
||||
quote! {
|
||||
impl TryFrom<crate::v0::MultiLocation> for MultiLocation {
|
||||
type Error = ();
|
||||
fn try_from(v0: crate::v0::MultiLocation) -> core::result::Result<Self, ()> {
|
||||
use Junctions::*;
|
||||
match v0 {
|
||||
crate::v0::MultiLocation::Null => Ok(Here.into()),
|
||||
#match_variants
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user