feat: initialize Kurdistan SDK - independent fork of Polkadot SDK

This commit is contained in:
2025-12-13 15:44:15 +03:00
commit 286de54384
6841 changed files with 1848356 additions and 0 deletions
+36
View File
@@ -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
}
}
})
}
+97
View File
@@ -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()
}
+195
View File
@@ -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![_](Span::call_site())).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![_](Span::call_site())).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
})
}
}
}
}
}
+237
View File
@@ -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![_](Span::call_site())).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![_](Span::call_site())).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
})
}
}
}
}
}
+198
View File
@@ -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![_](Span::call_site())).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![_](Span::call_site())).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
})
}
}
}
}
}
+67
View File
@@ -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);
}
+31
View File
@@ -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;
| ^^^^^^^^^^^^^^^^^^