feat: initialize Kurdistan SDK - independent fork of Polkadot SDK
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
[package]
|
||||
name = "xcm-procedural"
|
||||
description = "Procedural macros for XCM"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
version = "7.0.0"
|
||||
publish = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
Inflector = { workspace = true }
|
||||
proc-macro2 = { workspace = true }
|
||||
quote = { workspace = true }
|
||||
syn = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
trybuild = { features = ["diff"], workspace = true }
|
||||
# NOTE: we have to explicitly specify `std` because of trybuild
|
||||
# https://github.com/paritytech/polkadot-sdk/pull/5167
|
||||
xcm = { workspace = true, default-features = true, features = ["std"] }
|
||||
# For testing macros.
|
||||
frame-support = { workspace = true }
|
||||
|
||||
[features]
|
||||
runtime-benchmarks = [
|
||||
"frame-support/runtime-benchmarks",
|
||||
"xcm/runtime-benchmarks",
|
||||
]
|
||||
@@ -0,0 +1,456 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Pezkuwi.
|
||||
|
||||
// Pezkuwi 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.
|
||||
|
||||
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Derive macro for creating XCMs with a builder pattern
|
||||
|
||||
use inflector::Inflector;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{
|
||||
Data, DataEnum, DeriveInput, Error, Expr, ExprLit, Field, Fields, GenericArgument, Ident, Lit,
|
||||
Meta, MetaNameValue, PathArguments, Result, Type, TypePath, Variant,
|
||||
};
|
||||
|
||||
pub fn derive(input: DeriveInput) -> Result<TokenStream2> {
|
||||
let data_enum = match &input.data {
|
||||
Data::Enum(data_enum) => data_enum,
|
||||
_ => return Err(Error::new_spanned(&input, "Expected the `Instruction` enum")),
|
||||
};
|
||||
let builder_raw_impl = generate_builder_raw_impl(&input.ident, data_enum)?;
|
||||
let builder_impl = generate_builder_impl(&input.ident, data_enum)?;
|
||||
let builder_unpaid_impl = generate_builder_unpaid_impl(&input.ident, data_enum)?;
|
||||
let output = quote! {
|
||||
/// A trait for types that track state inside the XcmBuilder
|
||||
pub trait XcmBuilderState {}
|
||||
|
||||
/// Access to all the instructions
|
||||
pub enum AnythingGoes {}
|
||||
/// You need to pay for execution
|
||||
pub enum PaymentRequired {}
|
||||
/// The holding register was loaded, now to buy execution
|
||||
pub enum LoadedHolding {}
|
||||
/// Need to explicitly state it won't pay for fees
|
||||
pub enum ExplicitUnpaidRequired {}
|
||||
|
||||
impl XcmBuilderState for AnythingGoes {}
|
||||
impl XcmBuilderState for PaymentRequired {}
|
||||
impl XcmBuilderState for LoadedHolding {}
|
||||
impl XcmBuilderState for ExplicitUnpaidRequired {}
|
||||
|
||||
/// Type used to build XCM programs
|
||||
pub struct XcmBuilder<Call, S: XcmBuilderState> {
|
||||
pub(crate) instructions: Vec<Instruction<Call>>,
|
||||
pub state: core::marker::PhantomData<S>,
|
||||
}
|
||||
|
||||
impl<Call> Xcm<Call> {
|
||||
pub fn builder() -> XcmBuilder<Call, PaymentRequired> {
|
||||
XcmBuilder::<Call, PaymentRequired> {
|
||||
instructions: Vec::new(),
|
||||
state: core::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
pub fn builder_unpaid() -> XcmBuilder<Call, ExplicitUnpaidRequired> {
|
||||
XcmBuilder::<Call, ExplicitUnpaidRequired> {
|
||||
instructions: Vec::new(),
|
||||
state: core::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
pub fn builder_unsafe() -> XcmBuilder<Call, AnythingGoes> {
|
||||
XcmBuilder::<Call, AnythingGoes> {
|
||||
instructions: Vec::new(),
|
||||
state: core::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
#builder_impl
|
||||
#builder_unpaid_impl
|
||||
#builder_raw_impl
|
||||
};
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
fn generate_builder_raw_impl(name: &Ident, data_enum: &DataEnum) -> Result<TokenStream2> {
|
||||
let methods = data_enum
|
||||
.variants
|
||||
.iter()
|
||||
.map(|variant| convert_variant_to_method(name, variant, None))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
let output = quote! {
|
||||
impl<Call> XcmBuilder<Call, AnythingGoes> {
|
||||
#(#methods)*
|
||||
|
||||
pub fn build(self) -> Xcm<Call> {
|
||||
Xcm(self.instructions)
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
fn generate_builder_impl(name: &Ident, data_enum: &DataEnum) -> Result<TokenStream2> {
|
||||
// We first require an instruction that load the holding register
|
||||
let load_holding_variants = data_enum
|
||||
.variants
|
||||
.iter()
|
||||
.map(|variant| {
|
||||
let maybe_builder_attr = variant.attrs.iter().find(|attr| match attr.meta {
|
||||
Meta::List(ref list) => list.path.is_ident("builder"),
|
||||
_ => false,
|
||||
});
|
||||
let builder_attr = match maybe_builder_attr {
|
||||
Some(builder) => builder.clone(),
|
||||
None => return Ok(None), /* It's not going to be an instruction that loads the
|
||||
* holding register */
|
||||
};
|
||||
let Meta::List(ref list) = builder_attr.meta else { unreachable!("We checked before") };
|
||||
let inner_ident: Ident = syn::parse2(list.tokens.clone()).map_err(|_| {
|
||||
Error::new_spanned(
|
||||
&builder_attr,
|
||||
"Expected `builder(loads_holding)` or `builder(pays_fees)`",
|
||||
)
|
||||
})?;
|
||||
let loads_holding_ident: Ident = syn::parse_quote!(loads_holding);
|
||||
let pays_fees_ident: Ident = syn::parse_quote!(pays_fees);
|
||||
if inner_ident == loads_holding_ident {
|
||||
Ok(Some(variant))
|
||||
} else if inner_ident == pays_fees_ident {
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(Error::new_spanned(
|
||||
&builder_attr,
|
||||
"Expected `builder(loads_holding)` or `builder(pays_fees)`",
|
||||
))
|
||||
}
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
let load_holding_methods = load_holding_variants
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(|variant| {
|
||||
let method = convert_variant_to_method(
|
||||
name,
|
||||
variant,
|
||||
Some(quote! { XcmBuilder<Call, LoadedHolding> }),
|
||||
)?;
|
||||
Ok(method)
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
let first_impl = quote! {
|
||||
impl<Call> XcmBuilder<Call, PaymentRequired> {
|
||||
#(#load_holding_methods)*
|
||||
}
|
||||
};
|
||||
|
||||
// Some operations are allowed after the holding register is loaded
|
||||
let allowed_after_load_holding_methods: Vec<TokenStream2> = data_enum
|
||||
.variants
|
||||
.iter()
|
||||
.filter(|variant| variant.ident == "ClearOrigin" || variant.ident == "SetHints")
|
||||
.map(|variant| {
|
||||
let method = convert_variant_to_method(name, variant, None)?;
|
||||
Ok(method)
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
// Then we require fees to be paid
|
||||
let pay_fees_variants = data_enum
|
||||
.variants
|
||||
.iter()
|
||||
.map(|variant| {
|
||||
let maybe_builder_attr = variant.attrs.iter().find(|attr| match attr.meta {
|
||||
Meta::List(ref list) => list.path.is_ident("builder"),
|
||||
_ => false,
|
||||
});
|
||||
let builder_attr = match maybe_builder_attr {
|
||||
Some(builder) => builder.clone(),
|
||||
None => return Ok(None), /* It's not going to be an instruction that pays fees */
|
||||
};
|
||||
let Meta::List(ref list) = builder_attr.meta else { unreachable!("We checked before") };
|
||||
let inner_ident: Ident = syn::parse2(list.tokens.clone()).map_err(|_| {
|
||||
Error::new_spanned(
|
||||
&builder_attr,
|
||||
"Expected `builder(loads_holding)` or `builder(pays_fees)`",
|
||||
)
|
||||
})?;
|
||||
let ident_to_match: Ident = syn::parse_quote!(pays_fees);
|
||||
if inner_ident == ident_to_match {
|
||||
Ok(Some(variant))
|
||||
} else {
|
||||
Ok(None) // Must have been `loads_holding` instead.
|
||||
}
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
let pay_fees_methods = pay_fees_variants
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(|variant| {
|
||||
let method = convert_variant_to_method(
|
||||
name,
|
||||
variant,
|
||||
Some(quote! { XcmBuilder<Call, AnythingGoes> }),
|
||||
)?;
|
||||
Ok(method)
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
let second_impl = quote! {
|
||||
impl<Call> XcmBuilder<Call, LoadedHolding> {
|
||||
#(#allowed_after_load_holding_methods)*
|
||||
#(#pay_fees_methods)*
|
||||
}
|
||||
};
|
||||
|
||||
let output = quote! {
|
||||
#first_impl
|
||||
#second_impl
|
||||
};
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
fn generate_builder_unpaid_impl(name: &Ident, data_enum: &DataEnum) -> Result<TokenStream2> {
|
||||
let unpaid_execution_variant = data_enum
|
||||
.variants
|
||||
.iter()
|
||||
.find(|variant| variant.ident == "UnpaidExecution")
|
||||
.ok_or(Error::new_spanned(&data_enum.variants, "No UnpaidExecution instruction"))?;
|
||||
let method = convert_variant_to_method(
|
||||
name,
|
||||
&unpaid_execution_variant,
|
||||
Some(quote! { XcmBuilder<Call, AnythingGoes> }),
|
||||
)?;
|
||||
Ok(quote! {
|
||||
impl<Call> XcmBuilder<Call, ExplicitUnpaidRequired> {
|
||||
#method
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Small helper enum to differentiate between fields that use a `BoundedVec`
|
||||
// and the rest.
|
||||
enum BoundedOrNormal {
|
||||
Normal(Field),
|
||||
Bounded(Field),
|
||||
}
|
||||
|
||||
// Have to call with `XcmBuilder<Call, LoadedHolding>` in allowed_after_load_holding_methods.
|
||||
fn convert_variant_to_method(
|
||||
name: &Ident,
|
||||
variant: &Variant,
|
||||
maybe_return_type: Option<TokenStream2>,
|
||||
) -> Result<TokenStream2> {
|
||||
let variant_name = &variant.ident;
|
||||
let method_name_string = &variant_name.to_string().to_snake_case();
|
||||
let method_name = syn::Ident::new(method_name_string, variant_name.span());
|
||||
let docs = get_doc_comments(variant);
|
||||
let method = match &variant.fields {
|
||||
Fields::Unit =>
|
||||
if let Some(return_type) = maybe_return_type {
|
||||
quote! {
|
||||
pub fn #method_name(self) -> #return_type {
|
||||
let mut new_instructions = self.instructions;
|
||||
new_instructions.push(#name::<Call>::#variant_name);
|
||||
XcmBuilder {
|
||||
instructions: new_instructions,
|
||||
state: core::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
pub fn #method_name(mut self) -> Self {
|
||||
self.instructions.push(#name::<Call>::#variant_name);
|
||||
self
|
||||
}
|
||||
}
|
||||
},
|
||||
Fields::Unnamed(fields) => {
|
||||
let arg_names: Vec<_> = fields
|
||||
.unnamed
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, _)| format_ident!("arg{}", index))
|
||||
.collect();
|
||||
let arg_types: Vec<_> = fields.unnamed.iter().map(|field| &field.ty).collect();
|
||||
if let Some(return_type) = maybe_return_type {
|
||||
quote! {
|
||||
pub fn #method_name(self, #(#arg_names: impl Into<#arg_types>),*) -> #return_type {
|
||||
let mut new_instructions = self.instructions;
|
||||
#(let #arg_names = #arg_names.into();)*
|
||||
new_instructions.push(#name::<Call>::#variant_name(#(#arg_names),*));
|
||||
XcmBuilder {
|
||||
instructions: new_instructions,
|
||||
state: core::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
pub fn #method_name(mut self, #(#arg_names: impl Into<#arg_types>),*) -> Self {
|
||||
#(let #arg_names = #arg_names.into();)*
|
||||
self.instructions.push(#name::<Call>::#variant_name(#(#arg_names),*));
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Fields::Named(fields) => {
|
||||
let fields: Vec<_> = fields
|
||||
.named
|
||||
.iter()
|
||||
.map(|field| {
|
||||
if let Type::Path(TypePath { path, .. }) = &field.ty {
|
||||
for segment in &path.segments {
|
||||
if segment.ident == format_ident!("BoundedVec") {
|
||||
return BoundedOrNormal::Bounded(field.clone());
|
||||
}
|
||||
}
|
||||
BoundedOrNormal::Normal(field.clone())
|
||||
} else {
|
||||
BoundedOrNormal::Normal(field.clone())
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let arg_names: Vec<_> = fields
|
||||
.iter()
|
||||
.map(|field| match field {
|
||||
BoundedOrNormal::Bounded(field) => &field.ident,
|
||||
BoundedOrNormal::Normal(field) => &field.ident,
|
||||
})
|
||||
.collect();
|
||||
let arg_types: Vec<_> = fields
|
||||
.iter()
|
||||
.map(|field| match field {
|
||||
BoundedOrNormal::Bounded(field) => {
|
||||
let inner_type =
|
||||
extract_generic_argument(&field.ty, 0, "BoundedVec's inner type")?;
|
||||
Ok(quote! {
|
||||
Vec<#inner_type>
|
||||
})
|
||||
},
|
||||
BoundedOrNormal::Normal(field) => {
|
||||
let inner_type = &field.ty;
|
||||
Ok(quote! {
|
||||
impl Into<#inner_type>
|
||||
})
|
||||
},
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
let bounded_names: Vec<_> = fields
|
||||
.iter()
|
||||
.filter_map(|field| match field {
|
||||
BoundedOrNormal::Bounded(field) => Some(&field.ident),
|
||||
BoundedOrNormal::Normal(_) => None,
|
||||
})
|
||||
.collect();
|
||||
let normal_names: Vec<_> = fields
|
||||
.iter()
|
||||
.filter_map(|field| match field {
|
||||
BoundedOrNormal::Normal(field) => Some(&field.ident),
|
||||
BoundedOrNormal::Bounded(_) => None,
|
||||
})
|
||||
.collect();
|
||||
let comma_in_the_middle = if normal_names.is_empty() {
|
||||
quote! {}
|
||||
} else {
|
||||
quote! {,}
|
||||
};
|
||||
if let Some(return_type) = maybe_return_type {
|
||||
quote! {
|
||||
pub fn #method_name(self, #(#arg_names: #arg_types),*) -> #return_type {
|
||||
let mut new_instructions = self.instructions;
|
||||
#(let #normal_names = #normal_names.into();)*
|
||||
#(let #bounded_names = BoundedVec::truncate_from(#bounded_names);)*
|
||||
new_instructions.push(#name::<Call>::#variant_name { #(#normal_names),* #comma_in_the_middle #(#bounded_names),* });
|
||||
XcmBuilder {
|
||||
instructions: new_instructions,
|
||||
state: core::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
pub fn #method_name(mut self, #(#arg_names: #arg_types),*) -> Self {
|
||||
#(let #normal_names = #normal_names.into();)*
|
||||
#(let #bounded_names = BoundedVec::truncate_from(#bounded_names);)*
|
||||
self.instructions.push(#name::<Call>::#variant_name { #(#normal_names),* #comma_in_the_middle #(#bounded_names),* });
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(quote! {
|
||||
#(#docs)*
|
||||
#method
|
||||
})
|
||||
}
|
||||
|
||||
fn get_doc_comments(variant: &Variant) -> Vec<TokenStream2> {
|
||||
variant
|
||||
.attrs
|
||||
.iter()
|
||||
.filter_map(|attr| match &attr.meta {
|
||||
Meta::NameValue(MetaNameValue {
|
||||
value: Expr::Lit(ExprLit { lit: Lit::Str(literal), .. }),
|
||||
..
|
||||
}) if attr.path().is_ident("doc") => Some(literal.value()),
|
||||
_ => None,
|
||||
})
|
||||
.map(|doc| syn::parse_str::<TokenStream2>(&format!("/// {}", doc)).unwrap())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn extract_generic_argument<'a>(
|
||||
field_ty: &'a Type,
|
||||
index: usize,
|
||||
expected_msg: &str,
|
||||
) -> Result<&'a Ident> {
|
||||
if let Type::Path(type_path) = field_ty {
|
||||
if let Some(segment) = type_path.path.segments.last() {
|
||||
if let PathArguments::AngleBracketed(angle_brackets) = &segment.arguments {
|
||||
let args: Vec<_> = angle_brackets.args.iter().collect();
|
||||
if let Some(GenericArgument::Type(Type::Path(TypePath { path, .. }))) =
|
||||
args.get(index)
|
||||
{
|
||||
return path.get_ident().ok_or_else(|| {
|
||||
Error::new_spanned(
|
||||
path,
|
||||
format!("Expected an identifier for {}", expected_msg),
|
||||
)
|
||||
});
|
||||
}
|
||||
return Err(Error::new_spanned(
|
||||
angle_brackets,
|
||||
format!("Expected a generic argument at index {} for {}", index, expected_msg),
|
||||
));
|
||||
}
|
||||
return Err(Error::new_spanned(
|
||||
&segment.arguments,
|
||||
format!("Expected angle-bracketed arguments for {}", expected_msg),
|
||||
));
|
||||
}
|
||||
return Err(Error::new_spanned(
|
||||
&type_path.path,
|
||||
format!("Expected at least one path segment for {}", expected_msg),
|
||||
));
|
||||
}
|
||||
Err(Error::new_spanned(field_ty, format!("Expected a path type for {}", expected_msg)))
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Pezkuwi.
|
||||
|
||||
// Pezkuwi 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.
|
||||
|
||||
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Simple derive macro for getting the number of variants in an enum.
|
||||
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{Data, DeriveInput, Error, Result};
|
||||
|
||||
pub fn derive(input: DeriveInput) -> Result<TokenStream2> {
|
||||
let data_enum = match &input.data {
|
||||
Data::Enum(data_enum) => data_enum,
|
||||
_ => return Err(Error::new_spanned(&input, "Expected an enum.")),
|
||||
};
|
||||
let ident = format_ident!("{}NumVariants", input.ident);
|
||||
let number_of_variants: usize = data_enum.variants.iter().count();
|
||||
Ok(quote! {
|
||||
pub struct #ident;
|
||||
impl ::frame_support::traits::Get<u32> for #ident {
|
||||
fn get() -> u32 {
|
||||
#number_of_variants as u32
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Pezkuwi.
|
||||
|
||||
// Pezkuwi 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.
|
||||
|
||||
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Procedural macros used in XCM.
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use syn::{parse_macro_input, DeriveInput};
|
||||
|
||||
mod builder_pattern;
|
||||
mod enum_variants;
|
||||
mod v3;
|
||||
mod v4;
|
||||
mod v5;
|
||||
mod weight_info;
|
||||
|
||||
#[proc_macro_derive(XcmWeightInfoTrait)]
|
||||
pub fn derive_xcm_weight_info(item: TokenStream) -> TokenStream {
|
||||
weight_info::derive(item)
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn impl_conversion_functions_for_multilocation_v3(input: TokenStream) -> TokenStream {
|
||||
v3::multilocation::generate_conversion_functions(input)
|
||||
.unwrap_or_else(syn::Error::into_compile_error)
|
||||
.into()
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn impl_conversion_functions_for_junctions_v3(input: TokenStream) -> TokenStream {
|
||||
v3::junctions::generate_conversion_functions(input)
|
||||
.unwrap_or_else(syn::Error::into_compile_error)
|
||||
.into()
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn impl_conversion_functions_for_location_v4(input: TokenStream) -> TokenStream {
|
||||
v4::location::generate_conversion_functions(input)
|
||||
.unwrap_or_else(syn::Error::into_compile_error)
|
||||
.into()
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn impl_conversion_functions_for_junctions_v4(input: TokenStream) -> TokenStream {
|
||||
v4::junctions::generate_conversion_functions(input)
|
||||
.unwrap_or_else(syn::Error::into_compile_error)
|
||||
.into()
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn impl_conversion_functions_for_junctions_v5(input: TokenStream) -> TokenStream {
|
||||
v5::junctions::generate_conversion_functions(input)
|
||||
.unwrap_or_else(syn::Error::into_compile_error)
|
||||
.into()
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn impl_conversion_functions_for_location_v5(input: TokenStream) -> TokenStream {
|
||||
v5::location::generate_conversion_functions(input)
|
||||
.unwrap_or_else(syn::Error::into_compile_error)
|
||||
.into()
|
||||
}
|
||||
|
||||
/// This is called on the `Instruction` enum, not on the `Xcm` struct,
|
||||
/// and allows for the following syntax for building XCMs:
|
||||
/// let message = Xcm::builder()
|
||||
/// .withdraw_asset(assets)
|
||||
/// .buy_execution(fees, weight_limit)
|
||||
/// .deposit_asset(assets, beneficiary)
|
||||
/// .build();
|
||||
#[proc_macro_derive(Builder, attributes(builder))]
|
||||
pub fn derive_builder(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
builder_pattern::derive(input)
|
||||
.unwrap_or_else(syn::Error::into_compile_error)
|
||||
.into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(NumVariants)]
|
||||
pub fn derive_num_variants(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
enum_variants::derive(input)
|
||||
.unwrap_or_else(syn::Error::into_compile_error)
|
||||
.into()
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Pezkuwi.
|
||||
|
||||
// Pezkuwi 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.
|
||||
|
||||
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{Result, Token};
|
||||
|
||||
const MAX_JUNCTIONS: usize = 8;
|
||||
|
||||
pub mod multilocation {
|
||||
use super::*;
|
||||
|
||||
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"));
|
||||
}
|
||||
|
||||
let from_tuples = generate_conversion_from_tuples(8, 8);
|
||||
|
||||
Ok(quote! {
|
||||
#from_tuples
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_conversion_from_tuples(max_junctions: usize, max_parents: usize) -> TokenStream {
|
||||
let mut from_tuples = (0..=max_junctions)
|
||||
.map(|num_junctions| {
|
||||
let types = (0..num_junctions).map(|i| format_ident!("J{}", i)).collect::<Vec<_>>();
|
||||
let idents =
|
||||
(0..num_junctions).map(|i| format_ident!("j{}", i)).collect::<Vec<_>>();
|
||||
let array_size = num_junctions;
|
||||
let interior = if num_junctions == 0 {
|
||||
quote!(Junctions::Here)
|
||||
} else {
|
||||
quote! {
|
||||
[#(#idents .into()),*].into()
|
||||
}
|
||||
};
|
||||
|
||||
let mut from_tuple = quote! {
|
||||
impl< #(#types : Into<Junction>,)* > From<( Ancestor, #( #types ),* )> for MultiLocation {
|
||||
fn from( ( Ancestor(parents), #(#idents),* ): ( Ancestor, #( #types ),* ) ) -> Self {
|
||||
MultiLocation { parents, interior: #interior }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[Junction; #array_size]> for MultiLocation {
|
||||
fn from(j: [Junction; #array_size]) -> Self {
|
||||
let [#(#idents),*] = j;
|
||||
MultiLocation { parents: 0, interior: #interior }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let from_parent_tuples = (0..=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< #(#types : Into<Junction>,)* > From<( #( #parents , )* #( #types , )* )> for MultiLocation {
|
||||
fn from( ( #(#underscores,)* #(#idents,)* ): ( #(#parents,)* #(#types,)* ) ) -> Self {
|
||||
Self { parents: #cur_parents as u8, interior: #interior }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
from_tuple.extend(from_parent_tuples);
|
||||
from_tuple
|
||||
})
|
||||
.collect::<TokenStream>();
|
||||
|
||||
let from_parent_junctions_tuples = (0..=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 as u8, interior: junctions }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
from_tuples.extend(from_parent_junctions_tuples);
|
||||
|
||||
quote! {
|
||||
impl From<(Ancestor, Junctions)> for MultiLocation {
|
||||
fn from((Ancestor(parents), interior): (Ancestor, Junctions)) -> Self {
|
||||
MultiLocation { parents, interior }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Junction> for MultiLocation {
|
||||
fn from(x: Junction) -> Self {
|
||||
MultiLocation { parents: 0, interior: [x].into() }
|
||||
}
|
||||
}
|
||||
|
||||
#from_tuples
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod junctions {
|
||||
use super::*;
|
||||
|
||||
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_v4 = generate_conversion_from_v4();
|
||||
let from_tuples = generate_conversion_from_tuples(MAX_JUNCTIONS);
|
||||
|
||||
Ok(quote! {
|
||||
#from_v4
|
||||
#from_tuples
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_conversion_from_tuples(max_junctions: usize) -> TokenStream {
|
||||
(1..=max_junctions)
|
||||
.map(|num_junctions| {
|
||||
let idents =
|
||||
(0..num_junctions).map(|i| format_ident!("j{}", i)).collect::<Vec<_>>();
|
||||
let types = (0..num_junctions).map(|i| format_ident!("J{}", i)).collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
impl<#(#types : Into<Junction>,)*> From<( #(#types,)* )> for Junctions {
|
||||
fn from( ( #(#idents,)* ): ( #(#types,)* ) ) -> Self {
|
||||
[#(#idents .into()),*].into()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn generate_conversion_from_v4() -> TokenStream {
|
||||
let match_variants = (0..8u8)
|
||||
.map(|current_number| {
|
||||
let number_ancestors = current_number + 1;
|
||||
let variant = format_ident!("X{}", number_ancestors);
|
||||
let idents =
|
||||
(0..=current_number).map(|i| format_ident!("j{}", i)).collect::<Vec<_>>();
|
||||
let convert = idents
|
||||
.iter()
|
||||
.map(|ident| {
|
||||
quote! { let #ident = core::convert::TryInto::try_into(#ident.clone())?; }
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
crate::v4::Junctions::#variant( junctions ) => {
|
||||
let [#(#idents),*] = &*junctions;
|
||||
#(#convert);*
|
||||
[#(#idents),*].into()
|
||||
},
|
||||
}
|
||||
})
|
||||
.collect::<TokenStream>();
|
||||
|
||||
quote! {
|
||||
impl core::convert::TryFrom<crate::v4::Junctions> for Junctions {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(mut new: crate::v4::Junctions) -> core::result::Result<Self, Self::Error> {
|
||||
use Junctions::*;
|
||||
Ok(match new {
|
||||
crate::v4::Junctions::Here => Here,
|
||||
#match_variants
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Pezkuwi.
|
||||
|
||||
// Pezkuwi 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.
|
||||
|
||||
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{Result, Token};
|
||||
|
||||
const MAX_JUNCTIONS: usize = 8;
|
||||
|
||||
pub mod location {
|
||||
use super::*;
|
||||
|
||||
/// Generates conversion functions from other types to the `Location` type:
|
||||
/// - [PalletInstance(50), GeneralIndex(1984)].into()
|
||||
/// - (Parent, Teyrchain(1000), AccountId32 { .. }).into()
|
||||
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"));
|
||||
}
|
||||
|
||||
let from_tuples = generate_conversion_from_tuples(8, 8);
|
||||
|
||||
Ok(quote! {
|
||||
#from_tuples
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_conversion_from_tuples(max_junctions: usize, max_parents: usize) -> TokenStream {
|
||||
let mut from_tuples = (0..=max_junctions)
|
||||
.map(|num_junctions| {
|
||||
let types = (0..num_junctions).map(|i| format_ident!("J{}", i)).collect::<Vec<_>>();
|
||||
let idents =
|
||||
(0..num_junctions).map(|i| format_ident!("j{}", i)).collect::<Vec<_>>();
|
||||
let array_size = num_junctions;
|
||||
let interior = if num_junctions == 0 {
|
||||
quote!(Junctions::Here)
|
||||
} else {
|
||||
let variant = format_ident!("X{}", num_junctions);
|
||||
quote! {
|
||||
Junctions::#variant( alloc::sync::Arc::new( [#(#idents .into()),*] ) )
|
||||
}
|
||||
};
|
||||
|
||||
let mut from_tuple = quote! {
|
||||
impl< #(#types : Into<Junction>,)* > From<( Ancestor, #( #types ),* )> for Location {
|
||||
fn from( ( Ancestor(parents), #(#idents),* ): ( Ancestor, #( #types ),* ) ) -> Self {
|
||||
Location { parents, interior: #interior }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[Junction; #array_size]> for Location {
|
||||
fn from(j: [Junction; #array_size]) -> Self {
|
||||
let [#(#idents),*] = j;
|
||||
Location { parents: 0, interior: #interior }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let from_parent_tuples = (0..=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< #(#types : Into<Junction>,)* > From<( #( #parents , )* #( #types , )* )> for Location {
|
||||
fn from( ( #(#underscores,)* #(#idents,)* ): ( #(#parents,)* #(#types,)* ) ) -> Self {
|
||||
Self { parents: #cur_parents as u8, interior: #interior }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
from_tuple.extend(from_parent_tuples);
|
||||
from_tuple
|
||||
})
|
||||
.collect::<TokenStream>();
|
||||
|
||||
let from_parent_junctions_tuples = (0..=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 Location {
|
||||
fn from( (#(#underscores,)* junctions): ( #(#parents,)* Junctions ) ) -> Self {
|
||||
Location { parents: #cur_parents as u8, interior: junctions }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
from_tuples.extend(from_parent_junctions_tuples);
|
||||
|
||||
quote! {
|
||||
impl From<(Ancestor, Junctions)> for Location {
|
||||
fn from((Ancestor(parents), interior): (Ancestor, Junctions)) -> Self {
|
||||
Location { parents, interior }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Junction> for Location {
|
||||
fn from(x: Junction) -> Self {
|
||||
Location { parents: 0, interior: [x].into() }
|
||||
}
|
||||
}
|
||||
|
||||
#from_tuples
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod junctions {
|
||||
use super::*;
|
||||
|
||||
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_v3 = generate_conversion_from_v3(MAX_JUNCTIONS);
|
||||
let from_v5 = generate_conversion_from_v5(MAX_JUNCTIONS);
|
||||
let from_tuples = generate_conversion_from_tuples(MAX_JUNCTIONS);
|
||||
|
||||
Ok(quote! {
|
||||
#from_v3
|
||||
#from_v5
|
||||
#from_tuples
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_conversion_from_tuples(max_junctions: usize) -> TokenStream {
|
||||
(1..=max_junctions)
|
||||
.map(|num_junctions| {
|
||||
let idents =
|
||||
(0..num_junctions).map(|i| format_ident!("j{}", i)).collect::<Vec<_>>();
|
||||
let types = (0..num_junctions).map(|i| format_ident!("J{}", i)).collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
impl<#(#types : Into<Junction>,)*> From<( #(#types,)* )> for Junctions {
|
||||
fn from( ( #(#idents,)* ): ( #(#types,)* ) ) -> Self {
|
||||
[#(#idents .into()),*].into()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn generate_conversion_from_v3(max_junctions: usize) -> TokenStream {
|
||||
let match_variants = (0..max_junctions)
|
||||
.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 convert = idents
|
||||
.iter()
|
||||
.map(|ident| {
|
||||
quote! { let #ident = core::convert::TryInto::try_into(#ident.clone())?; }
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
crate::v3::Junctions::#variant( #(#idents),* ) => {
|
||||
#(#convert);*;
|
||||
let junctions: Junctions = [#(#idents),*].into();
|
||||
junctions
|
||||
},
|
||||
}
|
||||
})
|
||||
.collect::<TokenStream>();
|
||||
|
||||
quote! {
|
||||
impl core::convert::TryFrom<crate::v3::Junctions> for Junctions {
|
||||
type Error = ();
|
||||
fn try_from(mut old: crate::v3::Junctions) -> core::result::Result<Self, ()> {
|
||||
Ok(match old {
|
||||
crate::v3::Junctions::Here => Junctions::Here,
|
||||
#match_variants
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_conversion_from_v5(max_junctions: usize) -> TokenStream {
|
||||
let match_variants = (0..max_junctions)
|
||||
.map(|current_number| {
|
||||
let number_ancestors = current_number + 1;
|
||||
let variant = format_ident!("X{}", number_ancestors);
|
||||
let idents =
|
||||
(0..=current_number).map(|i| format_ident!("j{}", i)).collect::<Vec<_>>();
|
||||
let convert = idents
|
||||
.iter()
|
||||
.map(|ident| {
|
||||
quote! { let #ident = core::convert::TryInto::try_into(#ident.clone())?; }
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
crate::v5::Junctions::#variant( junctions ) => {
|
||||
let [#(#idents),*] = &*junctions;
|
||||
#(#convert);*
|
||||
[#(#idents),*].into()
|
||||
},
|
||||
}
|
||||
})
|
||||
.collect::<TokenStream>();
|
||||
|
||||
quote! {
|
||||
impl core::convert::TryFrom<crate::v5::Junctions> for Junctions {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(mut new: crate::v5::Junctions) -> core::result::Result<Self, Self::Error> {
|
||||
use Junctions::*;
|
||||
Ok(match new {
|
||||
crate::v5::Junctions::Here => Here,
|
||||
#match_variants
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Pezkuwi.
|
||||
|
||||
// Pezkuwi 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.
|
||||
|
||||
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{Result, Token};
|
||||
|
||||
const MAX_JUNCTIONS: usize = 8;
|
||||
|
||||
pub mod location {
|
||||
use super::*;
|
||||
|
||||
/// Generates conversion functions from other types to the `Location` type:
|
||||
/// - [PalletInstance(50), GeneralIndex(1984)].into()
|
||||
/// - (Parent, Teyrchain(1000), AccountId32 { .. }).into()
|
||||
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"));
|
||||
}
|
||||
|
||||
let from_tuples = generate_conversion_from_tuples(8, 8);
|
||||
|
||||
Ok(quote! {
|
||||
#from_tuples
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_conversion_from_tuples(max_junctions: usize, max_parents: usize) -> TokenStream {
|
||||
let mut from_tuples = (0..=max_junctions)
|
||||
.map(|num_junctions| {
|
||||
let types = (0..num_junctions).map(|i| format_ident!("J{}", i)).collect::<Vec<_>>();
|
||||
let idents =
|
||||
(0..num_junctions).map(|i| format_ident!("j{}", i)).collect::<Vec<_>>();
|
||||
let array_size = num_junctions;
|
||||
let interior = if num_junctions == 0 {
|
||||
quote!(Junctions::Here)
|
||||
} else {
|
||||
let variant = format_ident!("X{}", num_junctions);
|
||||
quote! {
|
||||
Junctions::#variant( alloc::sync::Arc::new( [#(#idents .into()),*] ) )
|
||||
}
|
||||
};
|
||||
|
||||
let mut from_tuple = quote! {
|
||||
impl< #(#types : Into<Junction>,)* > From<( Ancestor, #( #types ),* )> for Location {
|
||||
fn from( ( Ancestor(parents), #(#idents),* ): ( Ancestor, #( #types ),* ) ) -> Self {
|
||||
Location { parents, interior: #interior }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[Junction; #array_size]> for Location {
|
||||
fn from(j: [Junction; #array_size]) -> Self {
|
||||
let [#(#idents),*] = j;
|
||||
Location { parents: 0, interior: #interior }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let from_parent_tuples = (0..=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< #(#types : Into<Junction>,)* > From<( #( #parents , )* #( #types , )* )> for Location {
|
||||
fn from( ( #(#underscores,)* #(#idents,)* ): ( #(#parents,)* #(#types,)* ) ) -> Self {
|
||||
Self { parents: #cur_parents as u8, interior: #interior }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
from_tuple.extend(from_parent_tuples);
|
||||
from_tuple
|
||||
})
|
||||
.collect::<TokenStream>();
|
||||
|
||||
let from_parent_junctions_tuples = (0..=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 Location {
|
||||
fn from( (#(#underscores,)* junctions): ( #(#parents,)* Junctions ) ) -> Self {
|
||||
Location { parents: #cur_parents as u8, interior: junctions }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
from_tuples.extend(from_parent_junctions_tuples);
|
||||
|
||||
quote! {
|
||||
impl From<(Ancestor, Junctions)> for Location {
|
||||
fn from((Ancestor(parents), interior): (Ancestor, Junctions)) -> Self {
|
||||
Location { parents, interior }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Junction> for Location {
|
||||
fn from(x: Junction) -> Self {
|
||||
Location { parents: 0, interior: [x].into() }
|
||||
}
|
||||
}
|
||||
|
||||
#from_tuples
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod junctions {
|
||||
use super::*;
|
||||
|
||||
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_v4 = generate_conversion_from_v4(MAX_JUNCTIONS);
|
||||
let from_tuples = generate_conversion_from_tuples(MAX_JUNCTIONS);
|
||||
|
||||
Ok(quote! {
|
||||
#from_v4
|
||||
#from_tuples
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_conversion_from_tuples(max_junctions: usize) -> TokenStream {
|
||||
(1..=max_junctions)
|
||||
.map(|num_junctions| {
|
||||
let idents =
|
||||
(0..num_junctions).map(|i| format_ident!("j{}", i)).collect::<Vec<_>>();
|
||||
let types = (0..num_junctions).map(|i| format_ident!("J{}", i)).collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
impl<#(#types : Into<Junction>,)*> From<( #(#types,)* )> for Junctions {
|
||||
fn from( ( #(#idents,)* ): ( #(#types,)* ) ) -> Self {
|
||||
[#(#idents .into()),*].into()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn generate_conversion_from_v4(max_junctions: usize) -> TokenStream {
|
||||
let match_variants = (0..max_junctions)
|
||||
.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 convert = idents
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, ident)| {
|
||||
quote! { let #ident = core::convert::TryInto::try_into(slice[#index].clone())?; }
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
crate::v4::Junctions::#variant( arc ) => {
|
||||
let slice = &arc[..];
|
||||
#(#convert);*;
|
||||
let junctions: Junctions = [#(#idents),*].into();
|
||||
junctions
|
||||
},
|
||||
}
|
||||
})
|
||||
.collect::<TokenStream>();
|
||||
|
||||
quote! {
|
||||
impl core::convert::TryFrom<crate::v4::Junctions> for Junctions {
|
||||
type Error = ();
|
||||
fn try_from(mut old: crate::v4::Junctions) -> core::result::Result<Self, ()> {
|
||||
Ok(match old {
|
||||
crate::v4::Junctions::Here => Junctions::Here,
|
||||
#match_variants
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Pezkuwi.
|
||||
|
||||
// Pezkuwi 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.
|
||||
|
||||
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use inflector::Inflector;
|
||||
use quote::format_ident;
|
||||
|
||||
pub fn derive(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input: syn::DeriveInput = match syn::parse(item) {
|
||||
Ok(input) => input,
|
||||
Err(e) => return e.into_compile_error().into(),
|
||||
};
|
||||
|
||||
let syn::DeriveInput { generics, data, .. } = input;
|
||||
|
||||
match data {
|
||||
syn::Data::Enum(syn::DataEnum { variants, .. }) => {
|
||||
let methods = variants.into_iter().map(|syn::Variant { ident, fields, .. }| {
|
||||
let snake_cased_ident = format_ident!("{}", ident.to_string().to_snake_case());
|
||||
let ref_fields =
|
||||
fields.into_iter().enumerate().map(|(idx, syn::Field { ident, ty, .. })| {
|
||||
let field_name = ident.unwrap_or_else(|| format_ident!("_{}", idx));
|
||||
let field_ty = match ty {
|
||||
syn::Type::Reference(r) => {
|
||||
// If the type is already a reference, do nothing
|
||||
quote::quote!(#r)
|
||||
},
|
||||
t => {
|
||||
// Otherwise, make it a reference
|
||||
quote::quote!(&#t)
|
||||
},
|
||||
};
|
||||
|
||||
quote::quote!(#field_name: #field_ty,)
|
||||
});
|
||||
quote::quote!(fn #snake_cased_ident( #(#ref_fields)* ) -> Weight;)
|
||||
});
|
||||
|
||||
let res = quote::quote! {
|
||||
pub trait XcmWeightInfo #generics {
|
||||
#(#methods)*
|
||||
}
|
||||
};
|
||||
res.into()
|
||||
},
|
||||
syn::Data::Struct(syn::DataStruct { struct_token, .. }) => {
|
||||
let msg = "structs are not supported by 'derive(XcmWeightInfo)'";
|
||||
syn::Error::new(struct_token.span, msg).into_compile_error().into()
|
||||
},
|
||||
syn::Data::Union(syn::DataUnion { union_token, .. }) => {
|
||||
let msg = "unions are not supported by 'derive(XcmWeightInfo)'";
|
||||
syn::Error::new(union_token.span, msg).into_compile_error().into()
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Pezkuwi.
|
||||
|
||||
// Pezkuwi 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.
|
||||
|
||||
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Test the methods generated by the Builder derive macro.
|
||||
//! Tests directly on the actual Xcm struct and Instruction enum.
|
||||
|
||||
use frame_support::BoundedVec;
|
||||
use xcm::latest::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn builder_pattern_works() {
|
||||
let asset: Asset = (Here, 100u128).into();
|
||||
let beneficiary: Location = [0u8; 32].into();
|
||||
let message: Xcm<()> = Xcm::builder()
|
||||
.receive_teleported_asset(asset.clone())
|
||||
.buy_execution(asset.clone(), Unlimited)
|
||||
.deposit_asset(asset.clone(), beneficiary.clone())
|
||||
.build();
|
||||
assert_eq!(
|
||||
message,
|
||||
Xcm(vec![
|
||||
ReceiveTeleportedAsset(asset.clone().into()),
|
||||
BuyExecution { fees: asset.clone(), weight_limit: Unlimited },
|
||||
DepositAsset { assets: asset.into(), beneficiary },
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_builder_requires_buy_execution() {
|
||||
let asset: Asset = (Here, 100u128).into();
|
||||
let beneficiary: Location = AccountId32 { id: [0u8; 32], network: None }.into();
|
||||
// This is invalid, since it doesn't pay for fees.
|
||||
// This is enforced by the runtime, because the build() method doesn't exist
|
||||
// on the resulting type.
|
||||
// let message: Xcm<()> = Xcm::builder()
|
||||
// .withdraw_asset(asset.clone().into())
|
||||
// .deposit_asset(asset.into(), beneficiary)
|
||||
// .build();
|
||||
|
||||
// To be able to do that, we need to use the explicitly unpaid variant
|
||||
let message: Xcm<()> = Xcm::builder_unpaid()
|
||||
.unpaid_execution(Unlimited, None)
|
||||
.withdraw_asset(asset.clone())
|
||||
.deposit_asset(asset.clone(), beneficiary.clone())
|
||||
.build(); // This works
|
||||
assert_eq!(
|
||||
message,
|
||||
Xcm(vec![
|
||||
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
|
||||
WithdrawAsset(asset.clone().into()),
|
||||
DepositAsset { assets: asset.clone().into(), beneficiary: beneficiary.clone() },
|
||||
])
|
||||
);
|
||||
|
||||
// The other option doesn't have any limits whatsoever, so it should
|
||||
// only be used when you really know what you're doing.
|
||||
let message: Xcm<()> = Xcm::builder_unsafe()
|
||||
.withdraw_asset(asset.clone())
|
||||
.deposit_asset(asset.clone(), beneficiary.clone())
|
||||
.build();
|
||||
assert_eq!(
|
||||
message,
|
||||
Xcm(vec![
|
||||
WithdrawAsset(asset.clone().into()),
|
||||
DepositAsset { assets: asset.clone().into(), beneficiary },
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_builder_allows_clear_origin_before_buy_execution() {
|
||||
let asset: Asset = (Here, 100u128).into();
|
||||
let beneficiary: Location = [0u8; 32].into();
|
||||
let message: Xcm<()> = Xcm::builder()
|
||||
.receive_teleported_asset(asset.clone())
|
||||
.clear_origin()
|
||||
.buy_execution(asset.clone(), Unlimited)
|
||||
.deposit_asset(asset.clone(), beneficiary.clone())
|
||||
.build();
|
||||
assert_eq!(
|
||||
message,
|
||||
Xcm(vec![
|
||||
ReceiveTeleportedAsset(asset.clone().into()),
|
||||
ClearOrigin,
|
||||
BuyExecution { fees: asset.clone(), weight_limit: Unlimited },
|
||||
DepositAsset { assets: asset.into(), beneficiary },
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bounded_vecs_use_vecs_and_truncate_them() {
|
||||
let claimer = Location::parent();
|
||||
// We can use a vec instead of a bounded vec for specifying hints.
|
||||
let xcm: Xcm<()> = Xcm::builder_unsafe()
|
||||
.set_hints(vec![AssetClaimer { location: claimer.clone() }])
|
||||
.build();
|
||||
assert_eq!(
|
||||
xcm,
|
||||
Xcm(vec![SetHints {
|
||||
hints: BoundedVec::<Hint, HintNumVariants>::truncate_from(vec![AssetClaimer {
|
||||
location: Location::parent()
|
||||
},]),
|
||||
},])
|
||||
);
|
||||
|
||||
// If we include more than the limit they'll get truncated.
|
||||
let xcm: Xcm<()> = Xcm::builder_unsafe()
|
||||
.set_hints(vec![
|
||||
AssetClaimer { location: claimer.clone() },
|
||||
AssetClaimer { location: Location::here() },
|
||||
])
|
||||
.build();
|
||||
assert_eq!(
|
||||
xcm,
|
||||
Xcm(vec![SetHints {
|
||||
hints: BoundedVec::<Hint, HintNumVariants>::truncate_from(vec![AssetClaimer {
|
||||
location: Location::parent()
|
||||
},]),
|
||||
},])
|
||||
);
|
||||
|
||||
let xcm: Xcm<()> = Xcm::builder()
|
||||
.withdraw_asset((Here, 100u128))
|
||||
.set_hints(vec![AssetClaimer { location: claimer }])
|
||||
.clear_origin()
|
||||
.pay_fees((Here, 10u128))
|
||||
.deposit_asset(All, [0u8; 32])
|
||||
.build();
|
||||
assert_eq!(
|
||||
xcm,
|
||||
Xcm(vec![
|
||||
WithdrawAsset(Asset { id: AssetId(Location::here()), fun: Fungible(100) }.into()),
|
||||
SetHints {
|
||||
hints: BoundedVec::<Hint, HintNumVariants>::truncate_from(vec![AssetClaimer {
|
||||
location: Location::parent()
|
||||
}])
|
||||
},
|
||||
ClearOrigin,
|
||||
PayFees { asset: Asset { id: AssetId(Location::here()), fun: Fungible(10) } },
|
||||
DepositAsset {
|
||||
assets: All.into(),
|
||||
beneficiary: AccountId32 { id: [0u8; 32], network: None }.into()
|
||||
},
|
||||
])
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Pezkuwi.
|
||||
|
||||
// Pezkuwi 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.
|
||||
|
||||
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use xcm::v3::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn slice_syntax_in_v3_works() {
|
||||
let old_junctions = Junctions::X2(Teyrchain(1), PalletInstance(1));
|
||||
let new_junctions = Junctions::from([Teyrchain(1), PalletInstance(1)]);
|
||||
assert_eq!(old_junctions, new_junctions);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Pezkuwi.
|
||||
|
||||
// Pezkuwi 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.
|
||||
|
||||
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Test the struct generated by the `NumVariants` derive macro.
|
||||
|
||||
use frame_support::traits::Get;
|
||||
use xcm_procedural::NumVariants;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(NumVariants)]
|
||||
enum SomeEnum {
|
||||
Variant1,
|
||||
Variant2,
|
||||
Variant3,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn num_variants_works() {
|
||||
assert_eq!(SomeEnumNumVariants::get(), 3);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Pezkuwi.
|
||||
|
||||
// Pezkuwi 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.
|
||||
|
||||
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! UI tests for XCM procedural macros
|
||||
|
||||
#[test]
|
||||
fn ui() {
|
||||
// Only run the ui tests when `RUN_UI_TESTS` is set.
|
||||
if std::env::var("RUN_UI_TESTS").is_err() {
|
||||
return;
|
||||
}
|
||||
|
||||
// As trybuild is using `cargo check`, we don't need the real WASM binaries.
|
||||
std::env::set_var("SKIP_WASM_BUILD", "1");
|
||||
|
||||
let t = trybuild::TestCases::new();
|
||||
t.compile_fail("tests/ui/**/*.rs");
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Pezkuwi.
|
||||
|
||||
// Pezkuwi 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.
|
||||
|
||||
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Test error when using a badly formatted attribute.
|
||||
|
||||
use xcm_procedural::Builder;
|
||||
|
||||
struct Xcm<Call>(pub Vec<Instruction<Call>>);
|
||||
|
||||
#[derive(Builder)]
|
||||
enum Instruction<Call> {
|
||||
#[builder(funds_holding = 2)]
|
||||
WithdrawAsset(u128),
|
||||
BuyExecution { fees: u128 },
|
||||
UnpaidExecution { weight_limit: (u32, u32) },
|
||||
Transact { call: Call },
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: Expected `builder(loads_holding)` or `builder(pays_fees)`
|
||||
--> tests/ui/builder_pattern/badly_formatted_attribute.rs:25:5
|
||||
|
|
||||
25 | #[builder(funds_holding = 2)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Pezkuwi.
|
||||
|
||||
// Pezkuwi 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.
|
||||
|
||||
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Test error when there's no `UnpaidExecution` instruction.
|
||||
|
||||
use xcm_procedural::Builder;
|
||||
|
||||
struct Xcm<Call>(pub Vec<Instruction<Call>>);
|
||||
|
||||
#[derive(Builder)]
|
||||
enum Instruction<Call> {
|
||||
BuyExecution { fees: u128 },
|
||||
Transact { call: Call },
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,6 @@
|
||||
error: No UnpaidExecution instruction
|
||||
--> tests/ui/builder_pattern/no_unpaid_execution.rs:25:5
|
||||
|
|
||||
25 | / BuyExecution { fees: u128 },
|
||||
26 | | Transact { call: Call },
|
||||
| |____________________________^
|
||||
@@ -0,0 +1,5 @@
|
||||
error: Expected `builder(loads_holding)` or `builder(pays_fees)`
|
||||
--> tests/ui/builder_pattern/unexpected_attribute.rs:25:5
|
||||
|
|
||||
25 | #[builder(funds_holding)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Pezkuwi.
|
||||
|
||||
// Pezkuwi 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.
|
||||
|
||||
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Test error when attaching the derive builder macro to something
|
||||
//! other than the XCM `Instruction` enum.
|
||||
|
||||
use xcm_procedural::Builder;
|
||||
|
||||
#[derive(Builder)]
|
||||
struct SomeStruct;
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: Expected the `Instruction` enum
|
||||
--> tests/ui/builder_pattern/wrong_target.rs:23:1
|
||||
|
|
||||
23 | struct SomeStruct;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
Reference in New Issue
Block a user