feat: Rebrand Polkadot/Substrate references to PezkuwiChain

This commit systematically rebrands various references from Parity Technologies'
Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk.

Key changes include:
- Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks.
- Modified internal documentation and code comments to reflect PezkuwiChain naming and structure.
- Replaced direct references to  with  or specific paths within the  for XCM, Pezkuwi, and other modules.
- Cleaned up deprecated  issue and PR references in various  and  files, particularly in  and  modules.
- Adjusted image and logo URLs in documentation to point to PezkuwiChain assets.
- Removed or rephrased comments related to external Polkadot/Substrate PRs and issues.

This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
2025-12-14 00:04:10 +03:00
parent 286de54384
commit 1c0e57d984
9084 changed files with 997839 additions and 997557 deletions
@@ -0,0 +1,79 @@
[package]
name = "pezframe-support-procedural"
version = "23.0.0"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
homepage.workspace = true
repository.workspace = true
description = "Proc macro of Support code for the runtime."
[lints]
workspace = true
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[lib]
proc-macro = true
[[example]]
name = "proc_main"
[dependencies]
Inflector = { workspace = true }
cfg-expr = { workspace = true }
derive-syn-parse = { workspace = true }
docify = { workspace = true }
expander = { workspace = true }
pezframe-support-procedural-tools = { workspace = true, default-features = true }
itertools = { workspace = true }
macro_magic = { features = ["proc_support"], workspace = true }
proc-macro-warning = { workspace = true }
proc-macro2 = { workspace = true }
quote = { workspace = true }
pezsp-crypto-hashing = { workspace = true }
syn = { features = ["full", "parsing", "visit-mut"], workspace = true }
[dev-dependencies]
codec = { features = [
"derive",
"max-encoded-len",
], workspace = true, default-features = true }
pezframe-benchmarking = { workspace = true, default-features = true }
pezframe-support = { workspace = true, default-features = true }
pezframe-system = { workspace = true, default-features = true }
pretty_assertions = { workspace = true }
regex = { workspace = true }
scale-info = { features = [
"derive",
], workspace = true, default-features = true }
pezsp-io = { workspace = true, default-features = true }
pezsp-metadata-ir = { workspace = true, default-features = true }
pezsp-runtime = { features = ["serde"], workspace = true, default-features = true }
[features]
default = ["std"]
std = [
"codec/std",
"pezframe-benchmarking/std",
"pezframe-support/std",
"pezframe-system/std",
"scale-info/std",
"pezsp-crypto-hashing/std",
"pezsp-metadata-ir/std",
"pezsp-runtime/std",
]
no-metadata-docs = []
experimental = []
# Generate impl-trait for tuples with the given number of tuples. Will be needed as the number of
# pallets in a runtime grows. Does increase the compile time!
tuples-96 = []
tuples-128 = []
runtime-benchmarks = [
"pezframe-benchmarking/runtime-benchmarks",
"pezframe-support/runtime-benchmarks",
"pezframe-system/runtime-benchmarks",
"pezsp-io/runtime-benchmarks",
"pezsp-runtime/runtime-benchmarks",
]
@@ -0,0 +1,51 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#[cfg(test)]
use super::{Config, Runtime};
#[cfg(test)]
use crate::{derive_impl, pezpallet_prelude::inject_runtime_type};
#[cfg(test)]
use static_assertions::assert_type_eq_all;
#[docify::export]
#[test]
fn derive_impl_works_with_runtime_type_injection() {
assert_type_eq_all!(<Runtime as Config>::RuntimeOrigin, super::RuntimeOrigin);
assert_type_eq_all!(<Runtime as Config>::RuntimeCall, super::RuntimeCall);
assert_type_eq_all!(<Runtime as Config>::PalletInfo, super::PalletInfo);
}
#[docify::export]
#[test]
fn derive_impl_works_with_no_aggregated_types() {
struct DummyRuntime;
#[derive_impl(
super::pezframe_system::config_preludes::TestDefaultConfig as super::pezframe_system::DefaultConfig,
no_aggregated_types
)]
impl Config for DummyRuntime {
type Block = super::Block;
type AccountId = super::AccountId;
type PalletInfo = super::PalletInfo;
type ExampleConstant = ();
}
assert_type_eq_all!(<DummyRuntime as Config>::RuntimeOrigin, ());
assert_type_eq_all!(<DummyRuntime as Config>::RuntimeCall, ());
}
@@ -0,0 +1,738 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use pezframe_support::*;
use pezframe_support_procedural::import_section;
#[cfg(test)]
use pezsp_io::{MultiRemovalResults, TestExternalities};
#[cfg(test)]
use pezsp_metadata_ir::{
PalletStorageMetadataIR, StorageEntryMetadataIR, StorageEntryModifierIR, StorageEntryTypeIR,
StorageHasherIR,
};
#[cfg(test)]
use pezsp_runtime::BuildStorage;
use pezsp_runtime::{generic, traits::BlakeTwo256};
pub use self::pezframe_system::{pezpallet_prelude::*, Config, Pallet};
mod inject_runtime_type;
mod runtime;
mod tasks;
#[import_section(tasks::tasks_example)]
#[pallet]
pub mod pezframe_system {
#[allow(unused)]
use super::{pezframe_system, pezframe_system::pezpallet_prelude::*};
pub use crate::dispatch::RawOrigin;
use crate::{pezpallet_prelude::*, traits::tasks::Task as TaskTrait};
pub mod config_preludes {
use super::{inject_runtime_type, DefaultConfig};
pub struct TestDefaultConfig;
#[crate::register_default_impl(TestDefaultConfig)]
impl DefaultConfig for TestDefaultConfig {
type AccountId = u64;
type BaseCallFilter = pezframe_support::traits::Everything;
#[inject_runtime_type]
type RuntimeOrigin = ();
#[inject_runtime_type]
type RuntimeCall = ();
#[inject_runtime_type]
type PalletInfo = ();
#[inject_runtime_type]
type RuntimeTask = ();
type DbWeight = ();
}
}
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::config(with_default, pezframe_system_config)]
#[pallet::disable_pezframe_system_supertrait_check]
pub trait Config: 'static {
#[pallet::no_default]
type Block: Parameter + pezsp_runtime::traits::Block;
type AccountId;
#[pallet::no_default_bounds]
type BaseCallFilter: crate::traits::Contains<Self::RuntimeCall>;
#[pallet::no_default_bounds]
type RuntimeOrigin;
#[pallet::no_default_bounds]
type RuntimeCall;
#[pallet::no_default_bounds]
type RuntimeTask: crate::traits::tasks::Task;
#[pallet::no_default_bounds]
type PalletInfo: crate::traits::PalletInfo;
type DbWeight: Get<crate::weights::RuntimeDbWeight>;
}
#[pallet::error]
pub enum Error<T> {
/// Required by construct_runtime
CallFiltered,
/// Used in tasks example.
NotFound,
/// The specified [`Task`] is not valid.
InvalidTask,
/// The specified [`Task`] failed during execution.
FailedTask,
}
#[pallet::origin]
pub type Origin<T> = RawOrigin<<T as Config>::AccountId>;
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::call_index(0)]
#[pallet::weight(task.weight())]
pub fn do_task(_origin: OriginFor<T>, task: T::RuntimeTask) -> DispatchResultWithPostInfo {
if !task.is_valid() {
return Err(Error::<T>::InvalidTask.into());
}
if let Err(_err) = task.run() {
return Err(Error::<T>::FailedTask.into());
}
Ok(().into())
}
}
#[pallet::storage]
pub type Data<T> = StorageMap<_, Twox64Concat, u32, u64, ValueQuery>;
#[pallet::storage]
pub type OptionLinkedMap<T> = StorageMap<_, Blake2_128Concat, u32, u32, OptionQuery>;
#[pallet::storage]
pub type GenericData<T: Config> =
StorageMap<_, Identity, BlockNumberFor<T>, BlockNumberFor<T>, ValueQuery>;
#[pallet::storage]
pub type GenericData2<T: Config> =
StorageMap<_, Blake2_128Concat, BlockNumberFor<T>, BlockNumberFor<T>, OptionQuery>;
#[pallet::storage]
pub type DataDM<T> =
StorageDoubleMap<_, Twox64Concat, u32, Blake2_128Concat, u32, u64, ValueQuery>;
#[pallet::storage]
pub type GenericDataDM<T: Config> = StorageDoubleMap<
_,
Blake2_128Concat,
BlockNumberFor<T>,
Identity,
BlockNumberFor<T>,
BlockNumberFor<T>,
ValueQuery,
>;
#[pallet::storage]
pub type GenericData2DM<T: Config> = StorageDoubleMap<
_,
Blake2_128Concat,
BlockNumberFor<T>,
Twox64Concat,
BlockNumberFor<T>,
BlockNumberFor<T>,
OptionQuery,
>;
#[pallet::storage]
#[pallet::unbounded]
pub type AppendableDM<T: Config> = StorageDoubleMap<
_,
Blake2_128Concat,
u32,
Blake2_128Concat,
BlockNumberFor<T>,
Vec<u32>,
ValueQuery,
>;
#[pallet::genesis_config]
pub struct GenesisConfig<T: Config> {
pub data: Vec<(u32, u64)>,
pub test_config: Vec<(u32, u32, u64)>,
#[serde(skip)]
pub _config: core::marker::PhantomData<T>,
}
impl<T: Config> Default for GenesisConfig<T> {
fn default() -> Self {
Self {
_config: Default::default(),
data: vec![(15u32, 42u64)],
test_config: vec![(15u32, 16u32, 42u64)],
}
}
}
#[pallet::genesis_build]
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
fn build(&self) {
for (k, v) in &self.data {
<Data<T>>::insert(k, v);
}
for (k1, k2, v) in &self.test_config {
<DataDM<T>>::insert(k1, k2, v);
}
}
}
/// Some running total.
#[pallet::storage]
pub type Total<T: Config> = StorageValue<_, (u32, u32), ValueQuery>;
/// Numbers to be added into the total.
#[pallet::storage]
pub type Numbers<T: Config> = StorageMap<_, Twox64Concat, u32, u32, OptionQuery>;
pub mod pezpallet_prelude {
pub type OriginFor<T> = <T as super::Config>::RuntimeOrigin;
pub type HeaderFor<T> =
<<T as super::Config>::Block as pezsp_runtime::traits::HeaderProvider>::HeaderT;
pub type BlockNumberFor<T> = <HeaderFor<T> as pezsp_runtime::traits::Header>::Number;
}
}
type BlockNumber = u32;
type AccountId = u32;
type Header = generic::Header<BlockNumber, BlakeTwo256>;
type UncheckedExtrinsic = generic::UncheckedExtrinsic<u32, RuntimeCall, (), ()>;
type Block = generic::Block<Header, UncheckedExtrinsic>;
#[crate::runtime]
mod runtime {
#[runtime::runtime]
#[runtime::derive(
RuntimeCall,
RuntimeEvent,
RuntimeError,
RuntimeOrigin,
RuntimeFreezeReason,
RuntimeHoldReason,
RuntimeSlashReason,
RuntimeLockId,
RuntimeTask,
RuntimeViewFunction
)]
pub struct Runtime;
#[runtime::pezpallet_index(0)]
pub type System = self::pezframe_system;
}
#[crate::derive_impl(self::pezframe_system::config_preludes::TestDefaultConfig as self::pezframe_system::DefaultConfig)]
impl Config for Runtime {
type Block = Block;
type AccountId = AccountId;
}
#[cfg(test)]
fn new_test_ext() -> TestExternalities {
RuntimeGenesisConfig::default().build_storage().unwrap().into()
}
#[cfg(test)]
trait Sorted {
fn sorted(self) -> Self;
}
#[cfg(test)]
impl<T: Ord> Sorted for Vec<T> {
fn sorted(mut self) -> Self {
self.sort();
self
}
}
#[test]
fn map_issue_3318() {
new_test_ext().execute_with(|| {
type OptionLinkedMap = self::pezframe_system::OptionLinkedMap<Runtime>;
OptionLinkedMap::insert(1, 1);
assert_eq!(OptionLinkedMap::get(1), Some(1));
OptionLinkedMap::insert(1, 2);
assert_eq!(OptionLinkedMap::get(1), Some(2));
});
}
#[test]
fn map_swap_works() {
new_test_ext().execute_with(|| {
type OptionLinkedMap = self::pezframe_system::OptionLinkedMap<Runtime>;
OptionLinkedMap::insert(0, 0);
OptionLinkedMap::insert(1, 1);
OptionLinkedMap::insert(2, 2);
OptionLinkedMap::insert(3, 3);
let collect = || OptionLinkedMap::iter().collect::<Vec<_>>().sorted();
assert_eq!(collect(), vec![(0, 0), (1, 1), (2, 2), (3, 3)]);
// Two existing
OptionLinkedMap::swap(1, 2);
assert_eq!(collect(), vec![(0, 0), (1, 2), (2, 1), (3, 3)]);
// Back to normal
OptionLinkedMap::swap(2, 1);
assert_eq!(collect(), vec![(0, 0), (1, 1), (2, 2), (3, 3)]);
// Left existing
OptionLinkedMap::swap(2, 5);
assert_eq!(collect(), vec![(0, 0), (1, 1), (3, 3), (5, 2)]);
// Right existing
OptionLinkedMap::swap(5, 2);
assert_eq!(collect(), vec![(0, 0), (1, 1), (2, 2), (3, 3)]);
});
}
#[test]
fn double_map_swap_works() {
new_test_ext().execute_with(|| {
type DataDM = self::pezframe_system::DataDM<Runtime>;
DataDM::insert(0, 1, 1);
DataDM::insert(1, 0, 2);
DataDM::insert(1, 1, 3);
let get_all = || {
vec![
DataDM::get(0, 1),
DataDM::get(1, 0),
DataDM::get(1, 1),
DataDM::get(2, 0),
DataDM::get(2, 1),
]
};
assert_eq!(get_all(), vec![1, 2, 3, 0, 0]);
// Two existing
DataDM::swap(0, 1, 1, 0);
assert_eq!(get_all(), vec![2, 1, 3, 0, 0]);
// Left existing
DataDM::swap(1, 0, 2, 0);
assert_eq!(get_all(), vec![2, 0, 3, 1, 0]);
// Right existing
DataDM::swap(2, 1, 1, 1);
assert_eq!(get_all(), vec![2, 0, 0, 1, 3]);
});
}
#[test]
fn map_basic_insert_remove_should_work() {
new_test_ext().execute_with(|| {
type Map = self::pezframe_system::Data<Runtime>;
// initialized during genesis
assert_eq!(Map::get(&15u32), 42u64);
// get / insert / take
let key = 17u32;
assert_eq!(Map::get(&key), 0u64);
Map::insert(key, 4u64);
assert_eq!(Map::get(&key), 4u64);
assert_eq!(Map::take(&key), 4u64);
assert_eq!(Map::get(&key), 0u64);
// mutate
Map::mutate(&key, |val| {
*val = 15;
});
assert_eq!(Map::get(&key), 15u64);
// remove
Map::remove(&key);
assert_eq!(Map::get(&key), 0u64);
});
}
#[test]
fn map_iteration_should_work() {
new_test_ext().execute_with(|| {
type Map = self::pezframe_system::Data<Runtime>;
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(15, 42)]);
// insert / remove
let key = 17u32;
Map::insert(key, 4u64);
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(15, 42), (key, 4)]);
assert_eq!(Map::take(&15), 42u64);
assert_eq!(Map::take(&key), 4u64);
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![]);
// Add couple of more elements
Map::insert(key, 42u64);
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key, 42)]);
Map::insert(key + 1, 43u64);
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key, 42), (key + 1, 43)]);
// mutate
let key = key + 2;
Map::mutate(&key, |val| {
*val = 15;
});
assert_eq!(
Map::iter().collect::<Vec<_>>().sorted(),
vec![(key - 2, 42), (key - 1, 43), (key, 15)]
);
Map::mutate(&key, |val| {
*val = 17;
});
assert_eq!(
Map::iter().collect::<Vec<_>>().sorted(),
vec![(key - 2, 42), (key - 1, 43), (key, 17)]
);
// remove first
Map::remove(&key);
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key - 2, 42), (key - 1, 43)]);
// remove last from the list
Map::remove(&(key - 2));
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![(key - 1, 43)]);
// remove the last element
Map::remove(&(key - 1));
assert_eq!(Map::iter().collect::<Vec<_>>().sorted(), vec![]);
});
}
#[test]
fn double_map_basic_insert_remove_remove_prefix_with_commit_should_work() {
let key1 = 17u32;
let key2 = 18u32;
type DoubleMap = self::pezframe_system::DataDM<Runtime>;
let mut e = new_test_ext();
e.execute_with(|| {
// initialized during genesis
assert_eq!(DoubleMap::get(&15u32, &16u32), 42u64);
// get / insert / take
assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
DoubleMap::insert(&key1, &key2, &4u64);
assert_eq!(DoubleMap::get(&key1, &key2), 4u64);
assert_eq!(DoubleMap::take(&key1, &key2), 4u64);
assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
// mutate
DoubleMap::mutate(&key1, &key2, |val| *val = 15);
assert_eq!(DoubleMap::get(&key1, &key2), 15u64);
// remove
DoubleMap::remove(&key1, &key2);
assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
// remove prefix
DoubleMap::insert(&key1, &key2, &4u64);
DoubleMap::insert(&key1, &(key2 + 1), &4u64);
DoubleMap::insert(&(key1 + 1), &key2, &4u64);
DoubleMap::insert(&(key1 + 1), &(key2 + 1), &4u64);
});
e.commit_all().unwrap();
e.execute_with(|| {
assert!(matches!(
DoubleMap::clear_prefix(&key1, u32::max_value(), None),
MultiRemovalResults { maybe_cursor: None, backend: 2, unique: 2, loops: 2 }
));
assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
assert_eq!(DoubleMap::get(&key1, &(key2 + 1)), 0u64);
assert_eq!(DoubleMap::get(&(key1 + 1), &key2), 4u64);
assert_eq!(DoubleMap::get(&(key1 + 1), &(key2 + 1)), 4u64);
});
}
#[test]
fn double_map_basic_insert_remove_remove_prefix_should_work() {
new_test_ext().execute_with(|| {
let key1 = 17u32;
let key2 = 18u32;
type DoubleMap = self::pezframe_system::DataDM<Runtime>;
// initialized during genesis
assert_eq!(DoubleMap::get(&15u32, &16u32), 42u64);
// get / insert / take
assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
DoubleMap::insert(&key1, &key2, &4u64);
assert_eq!(DoubleMap::get(&key1, &key2), 4u64);
assert_eq!(DoubleMap::take(&key1, &key2), 4u64);
assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
// mutate
DoubleMap::mutate(&key1, &key2, |val| *val = 15);
assert_eq!(DoubleMap::get(&key1, &key2), 15u64);
// remove
DoubleMap::remove(&key1, &key2);
assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
// remove prefix
DoubleMap::insert(&key1, &key2, &4u64);
DoubleMap::insert(&key1, &(key2 + 1), &4u64);
DoubleMap::insert(&(key1 + 1), &key2, &4u64);
DoubleMap::insert(&(key1 + 1), &(key2 + 1), &4u64);
// all in overlay
assert!(matches!(
DoubleMap::clear_prefix(&key1, u32::max_value(), None),
MultiRemovalResults { maybe_cursor: None, backend: 0, unique: 0, loops: 0 }
));
// Note this is the incorrect answer (for now), since we are using v2 of
// `clear_prefix`.
// When we switch to v3, then this will become:
// MultiRemovalResults:: { maybe_cursor: None, backend: 0, unique: 2, loops: 2 },
assert!(matches!(
DoubleMap::clear_prefix(&key1, u32::max_value(), None),
MultiRemovalResults { maybe_cursor: None, backend: 0, unique: 0, loops: 0 }
));
assert_eq!(DoubleMap::get(&key1, &key2), 0u64);
assert_eq!(DoubleMap::get(&key1, &(key2 + 1)), 0u64);
assert_eq!(DoubleMap::get(&(key1 + 1), &key2), 4u64);
assert_eq!(DoubleMap::get(&(key1 + 1), &(key2 + 1)), 4u64);
});
}
#[test]
fn double_map_append_should_work() {
new_test_ext().execute_with(|| {
type DoubleMap = self::pezframe_system::AppendableDM<Runtime>;
let key1 = 17u32;
let key2 = 18u32;
DoubleMap::insert(&key1, &key2, &vec![1]);
DoubleMap::append(&key1, &key2, 2);
assert_eq!(DoubleMap::get(&key1, &key2), &[1, 2]);
});
}
#[test]
fn double_map_mutate_exists_should_work() {
new_test_ext().execute_with(|| {
type DoubleMap = self::pezframe_system::DataDM<Runtime>;
let (key1, key2) = (11, 13);
// mutated
DoubleMap::mutate_exists(key1, key2, |v| *v = Some(1));
assert_eq!(DoubleMap::get(&key1, key2), 1);
// removed if mutated to `None`
DoubleMap::mutate_exists(key1, key2, |v| *v = None);
assert!(!DoubleMap::contains_key(&key1, key2));
});
}
#[test]
fn double_map_try_mutate_exists_should_work() {
new_test_ext().execute_with(|| {
type DoubleMap = self::pezframe_system::DataDM<Runtime>;
type TestResult = Result<(), &'static str>;
let (key1, key2) = (11, 13);
// mutated if `Ok`
assert_ok!(DoubleMap::try_mutate_exists(key1, key2, |v| -> TestResult {
*v = Some(1);
Ok(())
}));
assert_eq!(DoubleMap::get(&key1, key2), 1);
// no-op if `Err`
assert_noop!(
DoubleMap::try_mutate_exists(key1, key2, |v| -> TestResult {
*v = Some(2);
Err("nah")
}),
"nah"
);
// removed if mutated to`None`
assert_ok!(DoubleMap::try_mutate_exists(key1, key2, |v| -> TestResult {
*v = None;
Ok(())
}));
assert!(!DoubleMap::contains_key(&key1, key2));
});
}
#[cfg(test)]
fn expected_metadata() -> PalletStorageMetadataIR {
PalletStorageMetadataIR {
prefix: "System",
entries: vec![
StorageEntryMetadataIR {
name: "Data",
modifier: StorageEntryModifierIR::Default,
ty: StorageEntryTypeIR::Map {
hashers: vec![StorageHasherIR::Twox64Concat],
key: scale_info::meta_type::<u32>(),
value: scale_info::meta_type::<u64>(),
},
default: vec![0, 0, 0, 0, 0, 0, 0, 0],
docs: vec![],
},
StorageEntryMetadataIR {
name: "OptionLinkedMap",
modifier: StorageEntryModifierIR::Optional,
ty: StorageEntryTypeIR::Map {
hashers: vec![StorageHasherIR::Blake2_128Concat],
key: scale_info::meta_type::<u32>(),
value: scale_info::meta_type::<u32>(),
},
default: vec![0],
docs: vec![],
},
StorageEntryMetadataIR {
name: "GenericData",
modifier: StorageEntryModifierIR::Default,
ty: StorageEntryTypeIR::Map {
hashers: vec![StorageHasherIR::Identity],
key: scale_info::meta_type::<u32>(),
value: scale_info::meta_type::<u32>(),
},
default: vec![0, 0, 0, 0],
docs: vec![],
},
StorageEntryMetadataIR {
name: "GenericData2",
modifier: StorageEntryModifierIR::Optional,
ty: StorageEntryTypeIR::Map {
hashers: vec![StorageHasherIR::Blake2_128Concat],
key: scale_info::meta_type::<u32>(),
value: scale_info::meta_type::<u32>(),
},
default: vec![0],
docs: vec![],
},
StorageEntryMetadataIR {
name: "DataDM",
modifier: StorageEntryModifierIR::Default,
ty: StorageEntryTypeIR::Map {
hashers: vec![StorageHasherIR::Twox64Concat, StorageHasherIR::Blake2_128Concat],
key: scale_info::meta_type::<(u32, u32)>(),
value: scale_info::meta_type::<u64>(),
},
default: vec![0, 0, 0, 0, 0, 0, 0, 0],
docs: vec![],
},
StorageEntryMetadataIR {
name: "GenericDataDM",
modifier: StorageEntryModifierIR::Default,
ty: StorageEntryTypeIR::Map {
hashers: vec![StorageHasherIR::Blake2_128Concat, StorageHasherIR::Identity],
key: scale_info::meta_type::<(u32, u32)>(),
value: scale_info::meta_type::<u32>(),
},
default: vec![0, 0, 0, 0],
docs: vec![],
},
StorageEntryMetadataIR {
name: "GenericData2DM",
modifier: StorageEntryModifierIR::Optional,
ty: StorageEntryTypeIR::Map {
hashers: vec![StorageHasherIR::Blake2_128Concat, StorageHasherIR::Twox64Concat],
key: scale_info::meta_type::<(u32, u32)>(),
value: scale_info::meta_type::<u32>(),
},
default: vec![0],
docs: vec![],
},
StorageEntryMetadataIR {
name: "AppendableDM",
modifier: StorageEntryModifierIR::Default,
ty: StorageEntryTypeIR::Map {
hashers: vec![
StorageHasherIR::Blake2_128Concat,
StorageHasherIR::Blake2_128Concat,
],
key: scale_info::meta_type::<(u32, u32)>(),
value: scale_info::meta_type::<Vec<u32>>(),
},
default: vec![0],
docs: vec![],
},
StorageEntryMetadataIR {
name: "Total",
modifier: StorageEntryModifierIR::Default,
ty: StorageEntryTypeIR::Plain(scale_info::meta_type::<(u32, u32)>()),
default: vec![0, 0, 0, 0, 0, 0, 0, 0],
docs: vec![" Some running total."],
},
StorageEntryMetadataIR {
name: "Numbers",
modifier: StorageEntryModifierIR::Optional,
ty: StorageEntryTypeIR::Map {
hashers: vec![StorageHasherIR::Twox64Concat],
key: scale_info::meta_type::<u32>(),
value: scale_info::meta_type::<u32>(),
},
default: vec![0],
docs: vec![" Numbers to be added into the total."],
},
],
}
}
#[test]
fn store_metadata() {
let metadata = Pallet::<Runtime>::storage_metadata();
pretty_assertions::assert_eq!(expected_metadata(), metadata);
}
parameter_types! {
storage StorageParameter: u64 = 10;
}
#[test]
fn check_storage_parameter_type_works() {
TestExternalities::default().execute_with(|| {
assert_eq!(pezsp_io::hashing::twox_128(b":StorageParameter:"), StorageParameter::key());
assert_eq!(10, StorageParameter::get());
StorageParameter::set(&300);
assert_eq!(300, StorageParameter::get());
})
}
#[test]
fn derive_partial_eq_no_bound_core_mod() {
mod core {}
#[derive(
crate::PartialEqNoBound,
crate::CloneNoBound,
crate::DebugNoBound,
crate::DefaultNoBound,
crate::EqNoBound,
)]
struct Test;
}
fn main() {}
@@ -0,0 +1,133 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#![allow(deprecated, clippy::deprecated_semver)]
use super::{pezframe_system, Block};
use crate::derive_impl;
#[crate::pallet(dev_mode)]
mod pezpallet_basic {
use super::pezframe_system;
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config: pezframe_system::Config {}
}
impl pezpallet_basic::Config for Runtime {}
#[crate::pallet(dev_mode)]
mod pezpallet_with_disabled_call {
use super::pezframe_system;
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config: pezframe_system::Config {}
}
impl pezpallet_with_disabled_call::Config for Runtime {}
#[crate::pallet(dev_mode)]
mod pezpallet_with_disabled_unsigned {
use super::pezframe_system;
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config: pezframe_system::Config {}
}
impl pezpallet_with_disabled_unsigned::Config for Runtime {}
#[crate::pallet]
mod pezpallet_with_instance {
use super::pezframe_system;
#[pallet::pallet]
pub struct Pallet<T, I = ()>(_);
#[pallet::config]
pub trait Config<I: 'static = ()>: pezframe_system::Config {}
}
#[allow(unused)]
type Instance1 = pezpallet_with_instance::Pallet<pezpallet_with_instance::Instance1>;
impl pezpallet_with_instance::Config<pezpallet_with_instance::Instance1> for Runtime {}
#[allow(unused)]
type Instance2 = pezpallet_with_instance::Pallet<pezpallet_with_instance::Instance2>;
impl pezpallet_with_instance::Config<pezpallet_with_instance::Instance2> for Runtime {}
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
impl pezframe_system::Config for Runtime {
type Block = Block;
}
#[docify::export(runtime_macro)]
#[crate::runtime]
mod runtime {
// The main runtime
#[runtime::runtime]
// Runtime Types to be generated
#[runtime::derive(
RuntimeCall,
RuntimeEvent,
RuntimeError,
RuntimeOrigin,
RuntimeFreezeReason,
RuntimeHoldReason,
RuntimeSlashReason,
RuntimeLockId,
RuntimeTask,
RuntimeViewFunction
)]
pub struct Runtime;
// Use the concrete pallet type
#[runtime::pezpallet_index(0)]
pub type System = pezframe_system::Pallet<Runtime>;
// Use path to the pallet
#[runtime::pezpallet_index(1)]
pub type Basic = pezpallet_basic;
// Use the concrete pallet type with instance
#[runtime::pezpallet_index(2)]
pub type PalletWithInstance1 = pezpallet_with_instance::Pallet<Runtime, Instance1>;
// Use path to the pallet with instance
#[runtime::pezpallet_index(3)]
pub type PalletWithInstance2 = pezpallet_with_instance<Instance2>;
// Ensure that the runtime does not export the calls from the pallet
#[runtime::pezpallet_index(4)]
#[runtime::disable_call]
#[deprecated = "example"]
pub type PalletWithDisabledCall = pezpallet_with_disabled_call::Pallet<Runtime>;
// Ensure that the runtime does not export the unsigned calls from the pallet
#[runtime::pezpallet_index(5)]
#[runtime::disable_unsigned]
pub type PalletWithDisabledUnsigned = pezpallet_with_disabled_unsigned::Pallet<Runtime>;
}
@@ -0,0 +1,61 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#[cfg(test)]
use super::{
assert_ok,
pezframe_system::{Numbers, Total},
Runtime, RuntimeOrigin, RuntimeTask, System,
};
use pezframe_support_procedural::pezpallet_section;
#[pezpallet_section]
mod tasks_example {
#[docify::export(tasks_example)]
#[pallet::tasks_experimental]
impl<T: Config> Pallet<T> {
/// Add a pair of numbers into the totals and remove them.
#[pallet::task_list(Numbers::<T>::iter_keys())]
#[pallet::task_condition(|i| Numbers::<T>::contains_key(i))]
#[pallet::task_weight(0.into())]
#[pallet::task_index(0)]
pub fn add_number_into_total(i: u32) -> DispatchResult {
let v = Numbers::<T>::take(i).ok_or(Error::<T>::NotFound)?;
Total::<T>::mutate(|(total_keys, total_values)| {
*total_keys += i;
*total_values += v;
});
Ok(())
}
}
}
#[docify::export]
#[test]
fn tasks_work() {
super::new_test_ext().execute_with(|| {
Numbers::<Runtime>::insert(0, 1);
let task = RuntimeTask::System(super::pezframe_system::Task::<Runtime>::AddNumberIntoTotal {
i: 0u32,
});
assert_ok!(System::do_task(RuntimeOrigin::signed(1), task.clone(),));
assert_eq!(Numbers::<Runtime>::get(0), None);
assert_eq!(Total::<Runtime>::get(), (0, 1));
});
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,250 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License
use crate::construct_runtime::Pallet;
use proc_macro2::TokenStream;
use quote::quote;
use syn::Ident;
pub fn expand_outer_dispatch(
runtime: &Ident,
system_pallet: &Pallet,
pezpallet_decls: &[Pallet],
scrate: &TokenStream,
) -> TokenStream {
let mut variant_defs = TokenStream::new();
let mut variant_patterns = Vec::new();
let mut variant_usages = Vec::new();
let mut query_call_part_macros = Vec::new();
let mut pezpallet_names = Vec::new();
let mut pezpallet_attrs = Vec::new();
let system_path = &system_pallet.path;
let pallets_with_call = pezpallet_decls.iter().filter(|decl| decl.exists_part("Call"));
for pezpallet_declaration in pallets_with_call {
let name = &pezpallet_declaration.name;
let path = &pezpallet_declaration.path;
let index = pezpallet_declaration.index;
let attr = pezpallet_declaration.get_attributes();
variant_defs.extend(quote! {
#attr
#[codec(index = #index)]
#name( #scrate::dispatch::CallableCallFor<#name, #runtime> ),
});
variant_usages.push(quote!( #scrate::dispatch::CallableCallFor<#name, #runtime> ));
variant_patterns.push(quote!(RuntimeCall::#name(call)));
pezpallet_names.push(name);
pezpallet_attrs.push(attr);
query_call_part_macros.push(quote! {
#path::__bizinikiwi_call_check::is_call_part_defined!(#name);
});
}
quote! {
#( #query_call_part_macros )*
/// The aggregated runtime call type.
#[derive(
Clone, PartialEq, Eq,
#scrate::__private::codec::Encode,
#scrate::__private::codec::Decode,
#scrate::__private::codec::DecodeWithMemTracking,
#scrate::__private::scale_info::TypeInfo,
#scrate::__private::RuntimeDebug,
)]
pub enum RuntimeCall {
#variant_defs
}
#[cfg(test)]
impl RuntimeCall {
/// Return a list of the module names together with their size in memory.
pub const fn sizes() -> &'static [( &'static str, usize )] {
use #scrate::dispatch::Callable;
use core::mem::size_of;
&[#(
#pezpallet_attrs
(
stringify!(#pezpallet_names),
size_of::< <#pezpallet_names as Callable<#runtime>>::RuntimeCall >(),
),
)*]
}
/// Panics with diagnostic information if the size is greater than the given `limit`.
pub fn assert_size_under(limit: usize) {
let size = core::mem::size_of::<Self>();
let call_oversize = size > limit;
if call_oversize {
println!("Size of `Call` is {} bytes (provided limit is {} bytes)", size, limit);
let mut sizes = Self::sizes().to_vec();
sizes.sort_by_key(|x| -(x.1 as isize));
for (i, &(name, size)) in sizes.iter().enumerate().take(5) {
println!("Offender #{}: {} at {} bytes", i + 1, name, size);
}
if let Some((_, next_size)) = sizes.get(5) {
println!("{} others of size {} bytes or less", sizes.len() - 5, next_size);
}
panic!(
"Size of `Call` is more than limit; use `Box` on complex parameter types to reduce the
size of `Call`.
If the limit is too strong, maybe consider providing a higher limit."
);
}
}
}
impl #scrate::dispatch::GetDispatchInfo for RuntimeCall {
fn get_dispatch_info(&self) -> #scrate::dispatch::DispatchInfo {
match self {
#(
#pezpallet_attrs
#variant_patterns => call.get_dispatch_info(),
)*
}
}
}
impl #scrate::dispatch::CheckIfFeeless for RuntimeCall {
type Origin = #system_path::pezpallet_prelude::OriginFor<#runtime>;
fn is_feeless(&self, origin: &Self::Origin) -> bool {
match self {
#(
#pezpallet_attrs
#variant_patterns => call.is_feeless(origin),
)*
}
}
}
impl #scrate::traits::GetCallMetadata for RuntimeCall {
fn get_call_metadata(&self) -> #scrate::traits::CallMetadata {
use #scrate::traits::GetCallName;
match self {
#(
#pezpallet_attrs
#variant_patterns => {
let function_name = call.get_call_name();
let pezpallet_name = stringify!(#pezpallet_names);
#scrate::traits::CallMetadata { function_name, pezpallet_name }
}
)*
}
}
fn get_module_names() -> &'static [&'static str] {
&[#(
#pezpallet_attrs
stringify!(#pezpallet_names),
)*]
}
fn get_call_names(module: &str) -> &'static [&'static str] {
use #scrate::{dispatch::Callable, traits::GetCallName};
match module {
#(
#pezpallet_attrs
stringify!(#pezpallet_names) =>
<<#pezpallet_names as Callable<#runtime>>::RuntimeCall
as GetCallName>::get_call_names(),
)*
_ => unreachable!(),
}
}
}
impl #scrate::__private::Dispatchable for RuntimeCall {
type RuntimeOrigin = RuntimeOrigin;
type Config = RuntimeCall;
type Info = #scrate::dispatch::DispatchInfo;
type PostInfo = #scrate::dispatch::PostDispatchInfo;
fn dispatch(self, origin: RuntimeOrigin) -> #scrate::dispatch::DispatchResultWithPostInfo {
if !<Self::RuntimeOrigin as #scrate::traits::OriginTrait>::filter_call(&origin, &self) {
return ::core::result::Result::Err(
#system_path::Error::<#runtime>::CallFiltered.into()
);
}
#scrate::traits::UnfilteredDispatchable::dispatch_bypass_filter(self, origin)
}
}
impl #scrate::traits::UnfilteredDispatchable for RuntimeCall {
type RuntimeOrigin = RuntimeOrigin;
fn dispatch_bypass_filter(self, origin: RuntimeOrigin) -> #scrate::dispatch::DispatchResultWithPostInfo {
match self {
#(
#pezpallet_attrs
#variant_patterns =>
#scrate::traits::UnfilteredDispatchable::dispatch_bypass_filter(call, origin),
)*
}
}
}
#(
#pezpallet_attrs
impl #scrate::traits::IsSubType<#scrate::dispatch::CallableCallFor<#pezpallet_names, #runtime>> for RuntimeCall {
#[allow(unreachable_patterns)]
fn is_sub_type(&self) -> Option<&#scrate::dispatch::CallableCallFor<#pezpallet_names, #runtime>> {
match self {
#variant_patterns => Some(call),
// May be unreachable
_ => None,
}
}
}
#pezpallet_attrs
impl From<#scrate::dispatch::CallableCallFor<#pezpallet_names, #runtime>> for RuntimeCall {
fn from(call: #scrate::dispatch::CallableCallFor<#pezpallet_names, #runtime>) -> Self {
#variant_patterns
}
}
)*
impl #scrate::traits::Authorize for RuntimeCall {
fn authorize(
&self,
source: #scrate::pezpallet_prelude::TransactionSource,
) -> ::core::option::Option<
::core::result::Result<
(
#scrate::pezpallet_prelude::ValidTransaction,
#scrate::pezpallet_prelude::Weight,
),
#scrate::pezpallet_prelude::TransactionValidityError
>
> {
match self {
#(
#pezpallet_attrs
#variant_patterns => #scrate::traits::Authorize::authorize(call, source),
)*
}
}
fn weight_of_authorize(&self) -> #scrate::pezpallet_prelude::Weight {
match self {
#(
#pezpallet_attrs
#variant_patterns =>
#scrate::traits::Authorize::weight_of_authorize(call),
)*
}
}
}
}
}
@@ -0,0 +1,101 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License
use crate::construct_runtime::parse::PalletPath;
use proc_macro2::{Ident, TokenStream};
use quote::quote;
pub(crate) fn expand_conversion_fn(
composite_name: &str,
path: &PalletPath,
instance: Option<&Ident>,
variant_name: &Ident,
) -> TokenStream {
let composite_name = quote::format_ident!("{}", composite_name);
let runtime_composite_name = quote::format_ident!("Runtime{}", composite_name);
if let Some(inst) = instance {
quote! {
impl From<#path::#composite_name<#path::#inst>> for #runtime_composite_name {
fn from(hr: #path::#composite_name<#path::#inst>) -> Self {
#runtime_composite_name::#variant_name(hr)
}
}
}
} else {
quote! {
impl From<#path::#composite_name> for #runtime_composite_name {
fn from(hr: #path::#composite_name) -> Self {
#runtime_composite_name::#variant_name(hr)
}
}
}
}
}
pub(crate) fn expand_variant(
composite_name: &str,
index: u8,
path: &PalletPath,
instance: Option<&Ident>,
variant_name: &Ident,
) -> TokenStream {
let composite_name = quote::format_ident!("{}", composite_name);
if let Some(inst) = instance {
quote! {
#[codec(index = #index)]
#variant_name(#path::#composite_name<#path::#inst>),
}
} else {
quote! {
#[codec(index = #index)]
#variant_name(#path::#composite_name),
}
}
}
pub(crate) fn expand_variant_count(
composite_name: &str,
path: &PalletPath,
instance: Option<&Ident>,
) -> TokenStream {
let composite_name = quote::format_ident!("{}", composite_name);
if let Some(inst) = instance {
quote! {
#path::#composite_name::<#path::#inst>::VARIANT_COUNT
}
} else {
// Wrapped `<`..`>` means: use default type parameter for enum.
//
// This is used for pallets without instance support or pallets with instance support when
// we don't specify instance:
//
// ```
// pub struct Pallet<T, I = ()>{..}
//
// #[pallet::composite_enum]
// pub enum HoldReason<I: 'static = ()> {..}
//
// Pallet1: pezpallet_x, // <- default type parameter
// ```
quote! {
<#path::#composite_name>::VARIANT_COUNT
}
}
}
@@ -0,0 +1,139 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License
use crate::construct_runtime::Pallet;
use inflector::Inflector;
use proc_macro2::TokenStream;
use quote::{format_ident, quote, ToTokens};
use syn::Ident;
pub fn expand_outer_config(
runtime: &Ident,
pezpallet_decls: &[Pallet],
scrate: &TokenStream,
) -> TokenStream {
let mut types = TokenStream::new();
let mut fields = TokenStream::new();
let mut genesis_build_calls = TokenStream::new();
let mut query_genesis_config_part_macros = Vec::new();
for decl in pezpallet_decls {
if let Some(pezpallet_entry) = decl.find_part("Config") {
let path = &decl.path;
let pezpallet_name = &decl.name;
let path_str = path.into_token_stream().to_string();
let config = format_ident!("{}Config", pezpallet_name);
let field_name =
&Ident::new(&pezpallet_name.to_string().to_snake_case(), decl.name.span());
let part_is_generic = !pezpallet_entry.generics.params.is_empty();
let attr = &decl.get_attributes();
types.extend(expand_config_types(attr, runtime, decl, &config, part_is_generic));
fields.extend(quote!(#attr pub #field_name: #config,));
genesis_build_calls
.extend(expand_config_build_storage_call(scrate, &config, attr, field_name));
query_genesis_config_part_macros.push(quote! {
#path::__bizinikiwi_genesis_config_check::is_genesis_config_defined!(#pezpallet_name);
#[cfg(feature = "std")]
#path::__bizinikiwi_genesis_config_check::is_std_enabled_for_genesis!(#pezpallet_name, #path_str);
});
}
}
quote! {
#( #query_genesis_config_part_macros )*
#types
use #scrate::__private::serde as __genesis_config_serde_import__;
#[derive(#scrate::__private::serde::Serialize, #scrate::__private::serde::Deserialize, Default)]
#[serde(rename_all = "camelCase")]
#[serde(deny_unknown_fields)]
#[serde(crate = "__genesis_config_serde_import__")]
pub struct RuntimeGenesisConfig {
#fields
}
#[cfg(any(feature = "std", test))]
impl #scrate::pezsp_runtime::BuildStorage for RuntimeGenesisConfig {
fn assimilate_storage(
&self,
storage: &mut #scrate::pezsp_runtime::Storage,
) -> std::result::Result<(), String> {
#scrate::__private::BasicExternalities::execute_with_storage(storage, || {
<Self as #scrate::traits::BuildGenesisConfig>::build(&self);
Ok(())
})
}
}
impl #scrate::traits::BuildGenesisConfig for RuntimeGenesisConfig {
fn build(&self) {
#genesis_build_calls
<AllPalletsWithSystem as #scrate::traits::OnGenesis>::on_genesis();
}
}
/// Test the `Default` derive impl of the `RuntimeGenesisConfig`.
#[cfg(test)]
#[test]
fn test_genesis_config_builds() {
#scrate::__private::pezsp_io::TestExternalities::default().execute_with(|| {
<RuntimeGenesisConfig as #scrate::traits::BuildGenesisConfig>::build(
&RuntimeGenesisConfig::default()
);
});
}
}
}
fn expand_config_types(
attr: &TokenStream,
runtime: &Ident,
decl: &Pallet,
config: &Ident,
part_is_generic: bool,
) -> TokenStream {
let path = &decl.path;
match (decl.instance.as_ref(), part_is_generic) {
(Some(inst), true) => quote! {
#attr
pub type #config = #path::GenesisConfig<#runtime, #path::#inst>;
},
(None, true) => quote! {
#attr
pub type #config = #path::GenesisConfig<#runtime>;
},
(_, false) => quote! {
#attr
pub type #config = #path::GenesisConfig;
},
}
}
fn expand_config_build_storage_call(
scrate: &TokenStream,
pezpallet_genesis_config: &Ident,
attr: &TokenStream,
field_name: &Ident,
) -> TokenStream {
quote! {
#attr
<#pezpallet_genesis_config as #scrate::traits::BuildGenesisConfig>::build(&self.#field_name);
}
}
@@ -0,0 +1,78 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License
use super::composite_helper;
use crate::construct_runtime::Pallet;
use proc_macro2::TokenStream;
use quote::quote;
pub fn expand_outer_freeze_reason(pezpallet_decls: &[Pallet], scrate: &TokenStream) -> TokenStream {
let mut conversion_fns = Vec::new();
let mut freeze_reason_variants = Vec::new();
let mut freeze_reason_variants_count = Vec::new();
for decl in pezpallet_decls {
if let Some(_) = decl.find_part("FreezeReason") {
let variant_name = &decl.name;
let path = &decl.path;
let index = decl.index;
let instance = decl.instance.as_ref();
conversion_fns.push(composite_helper::expand_conversion_fn(
"FreezeReason",
path,
instance,
variant_name,
));
freeze_reason_variants.push(composite_helper::expand_variant(
"FreezeReason",
index,
path,
instance,
variant_name,
));
freeze_reason_variants_count.push(composite_helper::expand_variant_count(
"FreezeReason",
path,
instance,
));
}
}
quote! {
/// A reason for placing a freeze on funds.
#[derive(
Copy, Clone, Eq, PartialEq,
#scrate::__private::codec::Encode,
#scrate::__private::codec::Decode,
#scrate::__private::codec::DecodeWithMemTracking,
#scrate::__private::codec::MaxEncodedLen,
#scrate::__private::scale_info::TypeInfo,
#scrate::__private::RuntimeDebug,
)]
pub enum RuntimeFreezeReason {
#( #freeze_reason_variants )*
}
impl #scrate::traits::VariantCount for RuntimeFreezeReason {
const VARIANT_COUNT: u32 = 0 #( + #freeze_reason_variants_count )*;
}
#( #conversion_fns )*
}
}
@@ -0,0 +1,78 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License
use super::composite_helper;
use crate::construct_runtime::Pallet;
use proc_macro2::TokenStream;
use quote::quote;
pub fn expand_outer_hold_reason(pezpallet_decls: &[Pallet], scrate: &TokenStream) -> TokenStream {
let mut conversion_fns = Vec::new();
let mut hold_reason_variants = Vec::new();
let mut hold_reason_variants_count = Vec::new();
for decl in pezpallet_decls {
if let Some(_) = decl.find_part("HoldReason") {
let variant_name = &decl.name;
let path = &decl.path;
let index = decl.index;
let instance = decl.instance.as_ref();
conversion_fns.push(composite_helper::expand_conversion_fn(
"HoldReason",
path,
instance,
variant_name,
));
hold_reason_variants.push(composite_helper::expand_variant(
"HoldReason",
index,
path,
instance,
variant_name,
));
hold_reason_variants_count.push(composite_helper::expand_variant_count(
"HoldReason",
path,
instance,
));
}
}
quote! {
/// A reason for placing a hold on funds.
#[derive(
Copy, Clone, Eq, PartialEq,
#scrate::__private::codec::Encode,
#scrate::__private::codec::Decode,
#scrate::__private::codec::DecodeWithMemTracking,
#scrate::__private::codec::MaxEncodedLen,
#scrate::__private::scale_info::TypeInfo,
#scrate::__private::RuntimeDebug,
)]
pub enum RuntimeHoldReason {
#( #hold_reason_variants )*
}
impl #scrate::traits::VariantCount for RuntimeHoldReason {
const VARIANT_COUNT: u32 = 0 #( + #hold_reason_variants_count )*;
}
#( #conversion_fns )*
}
}
@@ -0,0 +1,223 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License
use crate::construct_runtime::Pallet;
use proc_macro2::TokenStream;
use quote::quote;
use syn::Ident;
pub fn expand_outer_inherent(
runtime: &Ident,
block: &TokenStream,
unchecked_extrinsic: &TokenStream,
pezpallet_decls: &[Pallet],
scrate: &TokenStream,
) -> TokenStream {
let mut pezpallet_positions = Vec::new();
let mut pezpallet_names = Vec::new();
let mut pezpallet_attrs = Vec::new();
let mut query_inherent_part_macros = Vec::new();
for (pezpallet_pos, pezpallet_decl) in pezpallet_decls
.iter()
.filter(|pezpallet_decl| pezpallet_decl.exists_part("Inherent"))
.enumerate()
{
let name = &pezpallet_decl.name;
let path = &pezpallet_decl.path;
let attr = pezpallet_decl.get_attributes();
pezpallet_positions.push(pezpallet_pos);
pezpallet_names.push(name);
pezpallet_attrs.push(attr);
query_inherent_part_macros.push(quote! {
#path::__bizinikiwi_inherent_check::is_inherent_part_defined!(#name);
});
}
let pezpallet_count = pezpallet_positions.len();
quote! {
#( #query_inherent_part_macros )*
trait InherentDataExt {
fn create_extrinsics(&self) ->
#scrate::__private::Vec<<#block as #scrate::pezsp_runtime::traits::Block>::Extrinsic>;
fn check_extrinsics(
&self,
block: &<#block as #scrate::pezsp_runtime::traits::Block>::LazyBlock
) -> #scrate::inherent::CheckInherentsResult;
}
impl InherentDataExt for #scrate::inherent::InherentData {
fn create_extrinsics(&self) ->
#scrate::__private::Vec<<#block as #scrate::pezsp_runtime::traits::Block>::Extrinsic>
{
use #scrate::{inherent::ProvideInherent, traits::InherentBuilder};
let mut inherents = #scrate::__private::Vec::new();
#(
#pezpallet_attrs
if let Some(inherent) = #pezpallet_names::create_inherent(self) {
let inherent = <#unchecked_extrinsic as InherentBuilder>::new_inherent(
inherent.into(),
);
inherents.push(inherent);
}
)*
inherents
}
fn check_extrinsics(
&self,
block: &<#block as #scrate::pezsp_runtime::traits::Block>::LazyBlock
) ->
#scrate::inherent::CheckInherentsResult
{
use #scrate::inherent::{ProvideInherent, IsFatalError};
use #scrate::traits::IsSubType;
use #scrate::pezsp_runtime::traits::{Block as _, ExtrinsicCall, LazyBlock};
use #scrate::__private::{pezsp_inherents::Error, log};
let mut result = #scrate::inherent::CheckInherentsResult::new();
// This handle assume we abort on the first fatal error.
fn handle_put_error_result(res: Result<(), Error>) {
const LOG_TARGET: &str = "runtime::inherent";
match res {
Ok(()) => (),
Err(Error::InherentDataExists(id)) =>
log::debug!(
target: LOG_TARGET,
"Some error already reported for inherent {:?}, new non fatal \
error is ignored",
id
),
Err(Error::FatalErrorReported) =>
log::error!(
target: LOG_TARGET,
"Fatal error already reported, unexpected considering there is \
only one fatal error",
),
Err(_) =>
log::error!(
target: LOG_TARGET,
"Unexpected error from `put_error` operation",
),
}
}
let mut pezpallet_has_inherent = [false; #pezpallet_count];
for maybe_xt in block.extrinsics() {
let Ok(xt) = maybe_xt else {
panic!("check_extrinsics(): Unable to decode extrinsic");
};
// Inherents are before any other extrinsics.
// And signed extrinsics are not inherents.
if !(#scrate::pezsp_runtime::traits::ExtrinsicLike::is_bare(&xt)) {
break
}
let mut is_inherent = false;
let call = ExtrinsicCall::call(&xt);
#(
#pezpallet_attrs
{
if let Some(call) = IsSubType::<_>::is_sub_type(call) {
if #pezpallet_names::is_inherent(call) {
is_inherent = true;
pezpallet_has_inherent[#pezpallet_positions] = true;
if let Err(e) = #pezpallet_names::check_inherent(call, self) {
handle_put_error_result(result.put_error(
#pezpallet_names::INHERENT_IDENTIFIER, &e
));
if e.is_fatal_error() {
return result;
}
}
}
}
}
)*
// Inherents are before any other extrinsics.
// No module marked it as inherent, thus it is not.
if !is_inherent {
break
}
}
#(
#pezpallet_attrs
match #pezpallet_names::is_inherent_required(self) {
Ok(Some(e)) => {
if !pezpallet_has_inherent[#pezpallet_positions] {
handle_put_error_result(result.put_error(
#pezpallet_names::INHERENT_IDENTIFIER, &e
));
if e.is_fatal_error() {
return result;
}
}
},
Ok(None) => (),
Err(e) => {
handle_put_error_result(result.put_error(
#pezpallet_names::INHERENT_IDENTIFIER, &e
));
if e.is_fatal_error() {
return result;
}
},
}
)*
result
}
}
impl #scrate::traits::IsInherent<<#block as #scrate::pezsp_runtime::traits::Block>::Extrinsic> for #runtime {
fn is_inherent(ext: &<#block as #scrate::pezsp_runtime::traits::Block>::Extrinsic) -> bool {
use #scrate::inherent::ProvideInherent;
use #scrate::traits::IsSubType;
use #scrate::pezsp_runtime::traits::ExtrinsicCall;
let is_bare = #scrate::pezsp_runtime::traits::ExtrinsicLike::is_bare(ext);
if !is_bare {
// Inherents must be bare extrinsics.
return false
}
let call = ExtrinsicCall::call(ext);
#(
#pezpallet_attrs
{
if let Some(call) = IsSubType::<_>::is_sub_type(call) {
if <#pezpallet_names as ProvideInherent>::is_inherent(&call) {
return true;
}
}
}
)*
false
}
}
}
}
@@ -0,0 +1,67 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License
use super::composite_helper;
use crate::construct_runtime::Pallet;
use proc_macro2::TokenStream;
use quote::quote;
pub fn expand_outer_lock_id(pezpallet_decls: &[Pallet], scrate: &TokenStream) -> TokenStream {
let mut conversion_fns = Vec::new();
let mut lock_id_variants = Vec::new();
for decl in pezpallet_decls {
if let Some(_) = decl.find_part("LockId") {
let variant_name = &decl.name;
let path = &decl.path;
let index = decl.index;
let instance = decl.instance.as_ref();
conversion_fns.push(composite_helper::expand_conversion_fn(
"LockId",
path,
instance,
variant_name,
));
lock_id_variants.push(composite_helper::expand_variant(
"LockId",
index,
path,
instance,
variant_name,
));
}
}
quote! {
/// An identifier for each lock placed on funds.
#[derive(
Copy, Clone, Eq, PartialEq,
#scrate::__private::codec::Encode,
#scrate::__private::codec::Decode,
#scrate::__private::codec::DecodeWithMemTracking,
#scrate::__private::codec::MaxEncodedLen,
#scrate::__private::scale_info::TypeInfo,
#scrate::__private::RuntimeDebug,
)]
pub enum RuntimeLockId {
#( #lock_id_variants )*
}
#( #conversion_fns )*
}
}
@@ -0,0 +1,284 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License
use crate::construct_runtime::{parse::PalletPath, Pallet};
use proc_macro2::TokenStream;
use quote::quote;
use syn::Ident;
pub fn expand_runtime_metadata(
runtime: &Ident,
pezpallet_declarations: &[Pallet],
scrate: &TokenStream,
extrinsic: &TokenStream,
system_path: &PalletPath,
) -> TokenStream {
let pallets = pezpallet_declarations
.iter()
.filter_map(|pezpallet_declaration| {
pezpallet_declaration.find_part("Pallet").map(|_| {
let filtered_names: Vec<_> = pezpallet_declaration
.pezpallet_parts()
.iter()
.filter(|part| part.name() != "Pallet")
.map(|part| part.name())
.collect();
(pezpallet_declaration, filtered_names)
})
})
.map(|(decl, filtered_names)| {
let name = &decl.name;
let index = &decl.index;
let storage = expand_pallet_metadata_storage(&filtered_names, runtime, decl);
let calls = expand_pallet_metadata_calls(&filtered_names, runtime, decl);
let view_functions = expand_pallet_metadata_view_functions(runtime, decl);
let event = expand_pallet_metadata_events(&filtered_names, runtime, decl);
let constants = expand_pallet_metadata_constants(runtime, decl);
let errors = expand_pallet_metadata_errors(runtime, decl);
let associated_types = expand_pallet_metadata_associated_types(runtime, decl);
let docs = expand_pallet_metadata_docs(runtime, decl);
let attr = decl.get_attributes();
let deprecation_info = expand_pallet_metadata_deprecation(runtime, decl);
quote! {
#attr
#scrate::__private::metadata_ir::PalletMetadataIR {
name: stringify!(#name),
index: #index,
storage: #storage,
calls: #calls,
view_functions: #view_functions,
event: #event,
constants: #constants,
error: #errors,
docs: #docs,
associated_types: #associated_types,
deprecation_info: #deprecation_info,
}
}
})
.collect::<Vec<_>>();
quote! {
impl #runtime {
#[allow(deprecated)]
fn metadata_ir() -> #scrate::__private::metadata_ir::MetadataIR {
// Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata.
// The function is implemented by calling `impl_runtime_apis!`.
//
// However, the `construct_runtime!` may be called without calling `impl_runtime_apis!`.
// Rely on the `Deref` trait to differentiate between a runtime that implements
// APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro construct_runtime!).
//
// Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function.
// `InternalConstructRuntime` is implemented by the `construct_runtime!` for Runtime references (`& Runtime`),
// while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`).
//
// Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!`
// when both macros are called; and will resolve an empty `runtime_metadata` when only the `construct_runtime!`
// is called.
//
// `Deref` needs a reference for resolving the function call.
let rt = #runtime;
let ty = #scrate::__private::scale_info::meta_type::<#extrinsic>();
let address_ty = #scrate::__private::scale_info::meta_type::<
<#extrinsic as #scrate::traits::SignedTransactionBuilder>::Address
>();
let call_ty = #scrate::__private::scale_info::meta_type::<
<#extrinsic as #scrate::pezsp_runtime::traits::ExtrinsicCall>::Call
>();
let signature_ty = #scrate::__private::scale_info::meta_type::<
<#extrinsic as #scrate::traits::SignedTransactionBuilder>::Signature
>();
let extra_ty = #scrate::__private::scale_info::meta_type::<
<#extrinsic as #scrate::traits::SignedTransactionBuilder>::Extension
>();
use #scrate::__private::metadata_ir::InternalImplRuntimeApis;
#scrate::__private::metadata_ir::MetadataIR {
pallets: #scrate::__private::vec![ #(#pallets),* ],
extrinsic: #scrate::__private::metadata_ir::ExtrinsicMetadataIR {
ty,
versions: <#extrinsic as #scrate::pezsp_runtime::traits::ExtrinsicMetadata>::VERSIONS.into_iter().map(|ref_version| *ref_version).collect(),
address_ty,
call_ty,
signature_ty,
extra_ty,
extensions: <
<
#extrinsic as #scrate::pezsp_runtime::traits::ExtrinsicMetadata
>::TransactionExtensions
as
#scrate::pezsp_runtime::traits::TransactionExtension::<
<#runtime as #system_path::Config>::RuntimeCall
>
>::metadata()
.into_iter()
.map(|meta| #scrate::__private::metadata_ir::TransactionExtensionMetadataIR {
identifier: meta.identifier,
ty: meta.ty,
implicit: meta.implicit,
})
.collect(),
},
ty: #scrate::__private::scale_info::meta_type::<#runtime>(),
apis: (&rt).runtime_metadata(),
outer_enums: #scrate::__private::metadata_ir::OuterEnumsIR {
call_enum_ty: #scrate::__private::scale_info::meta_type::<
<#runtime as #system_path::Config>::RuntimeCall
>(),
event_enum_ty: #scrate::__private::scale_info::meta_type::<RuntimeEvent>(),
error_enum_ty: #scrate::__private::scale_info::meta_type::<RuntimeError>(),
},
}
}
pub fn metadata() -> #scrate::__private::metadata::RuntimeMetadataPrefixed {
// Note: this always returns the V14 version. The runtime API function
// must be deprecated.
#scrate::__private::metadata_ir::into_v14(#runtime::metadata_ir())
}
pub fn metadata_at_version(version: u32) -> Option<#scrate::__private::OpaqueMetadata> {
#scrate::__private::metadata_ir::into_version(#runtime::metadata_ir(), version).map(|prefixed| {
#scrate::__private::OpaqueMetadata::new(prefixed.into())
})
}
pub fn metadata_versions() -> #scrate::__private::Vec<u32> {
#scrate::__private::metadata_ir::supported_versions()
}
}
}
}
fn expand_pallet_metadata_storage(
filtered_names: &[&'static str],
runtime: &Ident,
decl: &Pallet,
) -> TokenStream {
if filtered_names.contains(&"Storage") {
let instance = decl.instance.as_ref().into_iter();
let path = &decl.path;
quote! {
Some(#path::Pallet::<#runtime #(, #path::#instance)*>::storage_metadata())
}
} else {
quote!(None)
}
}
fn expand_pallet_metadata_calls(
filtered_names: &[&'static str],
runtime: &Ident,
decl: &Pallet,
) -> TokenStream {
if filtered_names.contains(&"Call") {
let instance = decl.instance.as_ref().into_iter();
let path = &decl.path;
quote! {
Some(#path::Pallet::<#runtime #(, #path::#instance)*>::call_functions())
}
} else {
quote!(None)
}
}
fn expand_pallet_metadata_view_functions(runtime: &Ident, decl: &Pallet) -> TokenStream {
let path = &decl.path;
let instance = decl.instance.as_ref().into_iter();
quote! {
#path::Pallet::<#runtime #(, #path::#instance)*>::pezpallet_view_functions_metadata()
}
}
fn expand_pallet_metadata_events(
filtered_names: &[&'static str],
runtime: &Ident,
decl: &Pallet,
) -> TokenStream {
if filtered_names.contains(&"Event") {
let path = &decl.path;
let part_is_generic = !decl
.find_part("Event")
.expect("Event part exists; qed")
.generics
.params
.is_empty();
let pezpallet_event = match (decl.instance.as_ref(), part_is_generic) {
(Some(inst), true) => quote!(#path::Event::<#runtime, #path::#inst>),
(Some(inst), false) => quote!(#path::Event::<#path::#inst>),
(None, true) => quote!(#path::Event::<#runtime>),
(None, false) => quote!(#path::Event),
};
quote! {
Some(
#pezpallet_event::event_metadata::<#pezpallet_event>()
)
}
} else {
quote!(None)
}
}
fn expand_pallet_metadata_deprecation(runtime: &Ident, decl: &Pallet) -> TokenStream {
let path = &decl.path;
let instance = decl.instance.as_ref().into_iter();
quote! { #path::Pallet::<#runtime #(, #path::#instance)*>::deprecation_info() }
}
fn expand_pallet_metadata_constants(runtime: &Ident, decl: &Pallet) -> TokenStream {
let path = &decl.path;
let instance = decl.instance.as_ref().into_iter();
quote! {
#path::Pallet::<#runtime #(, #path::#instance)*>::pezpallet_constants_metadata()
}
}
fn expand_pallet_metadata_errors(runtime: &Ident, decl: &Pallet) -> TokenStream {
let path = &decl.path;
let instance = decl.instance.as_ref().into_iter();
quote! {
#path::Pallet::<#runtime #(, #path::#instance)*>::error_metadata()
}
}
fn expand_pallet_metadata_docs(runtime: &Ident, decl: &Pallet) -> TokenStream {
let path = &decl.path;
let instance = decl.instance.as_ref().into_iter();
quote! {
#path::Pallet::<#runtime #(, #path::#instance)*>::pezpallet_documentation_metadata()
}
}
fn expand_pallet_metadata_associated_types(runtime: &Ident, decl: &Pallet) -> TokenStream {
let path = &decl.path;
let instance = decl.instance.as_ref().into_iter();
quote! {
#path::Pallet::<#runtime #(, #path::#instance)*>::pezpallet_associated_types_metadata()
}
}
@@ -0,0 +1,45 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License
mod call;
pub mod composite_helper;
mod config;
mod freeze_reason;
mod hold_reason;
mod inherent;
mod lock_id;
mod metadata;
mod origin;
mod outer_enums;
mod slash_reason;
mod task;
mod unsigned;
mod view_function;
pub use call::expand_outer_dispatch;
pub use config::expand_outer_config;
pub use freeze_reason::expand_outer_freeze_reason;
pub use hold_reason::expand_outer_hold_reason;
pub use inherent::expand_outer_inherent;
pub use lock_id::expand_outer_lock_id;
pub use metadata::expand_runtime_metadata;
pub use origin::expand_outer_origin;
pub use outer_enums::{expand_outer_enum, OuterEnumType};
pub use slash_reason::expand_outer_slash_reason;
pub use task::expand_outer_task;
pub use unsigned::expand_outer_validate_unsigned;
pub use view_function::expand_outer_query;
@@ -0,0 +1,455 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License
use crate::construct_runtime::{Pallet, SYSTEM_PALLET_NAME};
use proc_macro2::TokenStream;
use quote::quote;
use syn::{Generics, Ident};
pub fn expand_outer_origin(
runtime: &Ident,
system_pallet: &Pallet,
pallets: &[Pallet],
scrate: &TokenStream,
) -> syn::Result<TokenStream> {
let mut caller_variants = TokenStream::new();
let mut pezpallet_conversions = TokenStream::new();
let mut query_origin_part_macros = Vec::new();
for pezpallet_decl in pallets.iter().filter(|pallet| pallet.name != SYSTEM_PALLET_NAME) {
if let Some(pezpallet_entry) = pezpallet_decl.find_part("Origin") {
let instance = pezpallet_decl.instance.as_ref();
let index = pezpallet_decl.index;
let generics = &pezpallet_entry.generics;
let name = &pezpallet_decl.name;
let path = &pezpallet_decl.path;
if instance.is_some() && generics.params.is_empty() {
let msg = format!(
"Instantiable pallet with no generic `Origin` cannot \
be constructed: pallet `{}` must have generic `Origin`",
name
);
return Err(syn::Error::new(name.span(), msg));
}
caller_variants.extend(expand_origin_caller_variant(
runtime,
pezpallet_decl,
index,
instance,
generics,
));
pezpallet_conversions.extend(expand_origin_pallet_conversions(
scrate,
runtime,
pezpallet_decl,
instance,
generics,
));
query_origin_part_macros.push(quote! {
#path::__bizinikiwi_origin_check::is_origin_part_defined!(#name);
});
}
}
let system_path = &system_pallet.path;
let system_index = system_pallet.index;
let system_path_name = system_path.module_name();
let doc_string = get_intra_doc_string(
"Origin is always created with the base filter configured in",
&system_path_name,
);
let doc_string_none_origin =
get_intra_doc_string("Create with system none origin and", &system_path_name);
let doc_string_root_origin =
get_intra_doc_string("Create with system root origin and", &system_path_name);
let doc_string_signed_origin =
get_intra_doc_string("Create with system signed origin and", &system_path_name);
let doc_string_runtime_origin =
get_intra_doc_string("Convert to runtime origin, using as filter:", &system_path_name);
let doc_string_runtime_origin_with_caller = get_intra_doc_string(
"Convert to runtime origin with caller being system signed or none and use filter",
&system_path_name,
);
Ok(quote! {
#( #query_origin_part_macros )*
/// The runtime origin type representing the origin of a call.
///
#[doc = #doc_string]
#[derive(Clone)]
pub struct RuntimeOrigin {
pub caller: OriginCaller,
filter: #scrate::__private::Rc<#scrate::__private::Box<dyn Fn(&<#runtime as #system_path::Config>::RuntimeCall) -> bool>>,
}
impl core::fmt::Debug for RuntimeOrigin {
fn fmt(
&self,
fmt: &mut core::fmt::Formatter,
) -> core::result::Result<(), core::fmt::Error> {
fmt.debug_struct("Origin")
.field("caller", &self.caller)
.field("filter", &"[function ptr]")
.finish()
}
}
impl #scrate::traits::OriginTrait for RuntimeOrigin {
type Call = <#runtime as #system_path::Config>::RuntimeCall;
type PalletsOrigin = OriginCaller;
type AccountId = <#runtime as #system_path::Config>::AccountId;
fn add_filter(&mut self, filter: impl Fn(&Self::Call) -> bool + 'static) {
let f = self.filter.clone();
self.filter = #scrate::__private::Rc::new(#scrate::__private::Box::new(move |call| {
f(call) && filter(call)
}));
}
fn reset_filter(&mut self) {
let filter = <
<#runtime as #system_path::Config>::BaseCallFilter
as #scrate::traits::Contains<<#runtime as #system_path::Config>::RuntimeCall>
>::contains;
self.filter = #scrate::__private::Rc::new(#scrate::__private::Box::new(filter));
}
fn set_caller(&mut self, caller: OriginCaller) {
self.caller = caller;
}
fn set_caller_from(&mut self, other: impl Into<Self>) {
self.caller = other.into().caller;
}
fn filter_call(&self, call: &Self::Call) -> bool {
match self.caller {
// Root bypasses all filters
OriginCaller::system(#system_path::Origin::<#runtime>::Root) => true,
_ => (self.filter)(call),
}
}
fn caller(&self) -> &Self::PalletsOrigin {
&self.caller
}
fn into_caller(self) -> Self::PalletsOrigin {
self.caller
}
fn try_with_caller<R>(
mut self,
f: impl FnOnce(Self::PalletsOrigin) -> Result<R, Self::PalletsOrigin>,
) -> Result<R, Self> {
match f(self.caller) {
Ok(r) => Ok(r),
Err(caller) => { self.caller = caller; Err(self) }
}
}
fn none() -> Self {
#system_path::RawOrigin::None.into()
}
fn root() -> Self {
#system_path::RawOrigin::Root.into()
}
fn signed(by: Self::AccountId) -> Self {
#system_path::RawOrigin::Signed(by).into()
}
}
#[derive(
Clone, PartialEq, Eq,
#scrate::__private::Debug,
#scrate::__private::codec::Encode,
#scrate::__private::codec::Decode,
#scrate::__private::codec::DecodeWithMemTracking,
#scrate::__private::scale_info::TypeInfo,
#scrate::__private::codec::MaxEncodedLen,
)]
#[allow(non_camel_case_types)]
pub enum OriginCaller {
#[codec(index = #system_index)]
system(#system_path::Origin<#runtime>),
#caller_variants
#[allow(dead_code)]
#[codec(skip)]
Void(#scrate::__private::Void)
}
// For backwards compatibility and ease of accessing these functions.
#[allow(dead_code)]
impl RuntimeOrigin {
#[doc = #doc_string_none_origin]
pub fn none() -> Self {
<RuntimeOrigin as #scrate::traits::OriginTrait>::none()
}
#[doc = #doc_string_root_origin]
pub fn root() -> Self {
<RuntimeOrigin as #scrate::traits::OriginTrait>::root()
}
#[doc = #doc_string_signed_origin]
pub fn signed(by: <#runtime as #system_path::Config>::AccountId) -> Self {
<RuntimeOrigin as #scrate::traits::OriginTrait>::signed(by)
}
}
impl From<#system_path::Origin<#runtime>> for OriginCaller {
fn from(x: #system_path::Origin<#runtime>) -> Self {
OriginCaller::system(x)
}
}
impl #scrate::traits::CallerTrait<<#runtime as #system_path::Config>::AccountId> for OriginCaller {
fn into_system(self) -> Option<#system_path::RawOrigin<<#runtime as #system_path::Config>::AccountId>> {
match self {
OriginCaller::system(x) => Some(x),
_ => None,
}
}
fn as_system_ref(&self) -> Option<&#system_path::RawOrigin<<#runtime as #system_path::Config>::AccountId>> {
match &self {
OriginCaller::system(o) => Some(o),
_ => None,
}
}
}
impl TryFrom<OriginCaller> for #system_path::Origin<#runtime> {
type Error = OriginCaller;
fn try_from(x: OriginCaller)
-> core::result::Result<#system_path::Origin<#runtime>, OriginCaller>
{
if let OriginCaller::system(l) = x {
Ok(l)
} else {
Err(x)
}
}
}
impl From<#system_path::Origin<#runtime>> for RuntimeOrigin {
#[doc = #doc_string_runtime_origin]
fn from(x: #system_path::Origin<#runtime>) -> Self {
let o: OriginCaller = x.into();
o.into()
}
}
impl From<OriginCaller> for RuntimeOrigin {
fn from(x: OriginCaller) -> Self {
let mut o = RuntimeOrigin {
caller: x,
filter: #scrate::__private::Rc::new(#scrate::__private::Box::new(|_| true)),
};
#scrate::traits::OriginTrait::reset_filter(&mut o);
o
}
}
impl From<RuntimeOrigin> for core::result::Result<#system_path::Origin<#runtime>, RuntimeOrigin> {
/// NOTE: converting to pallet origin loses the origin filter information.
fn from(val: RuntimeOrigin) -> Self {
if let OriginCaller::system(l) = val.caller {
Ok(l)
} else {
Err(val)
}
}
}
impl From<Option<<#runtime as #system_path::Config>::AccountId>> for RuntimeOrigin {
#[doc = #doc_string_runtime_origin_with_caller]
fn from(x: Option<<#runtime as #system_path::Config>::AccountId>) -> Self {
<#system_path::Origin<#runtime>>::from(x).into()
}
}
impl #scrate::__private::AsSystemOriginSigner<<#runtime as #system_path::Config>::AccountId> for RuntimeOrigin {
fn as_system_origin_signer(&self) -> Option<&<#runtime as #system_path::Config>::AccountId> {
if let OriginCaller::system(#system_path::Origin::<#runtime>::Signed(ref signed)) = &self.caller {
Some(signed)
} else {
None
}
}
}
impl #scrate::__private::AsTransactionAuthorizedOrigin for RuntimeOrigin {
fn is_transaction_authorized(&self) -> bool {
!matches!(&self.caller, OriginCaller::system(#system_path::Origin::<#runtime>::None))
}
}
#pezpallet_conversions
})
}
fn expand_origin_caller_variant(
runtime: &Ident,
pallet: &Pallet,
index: u8,
instance: Option<&Ident>,
generics: &Generics,
) -> TokenStream {
let part_is_generic = !generics.params.is_empty();
let variant_name = &pallet.name;
let path = &pallet.path;
let attr = pallet.get_attributes();
match instance {
Some(inst) if part_is_generic => quote! {
#attr
#[codec(index = #index)]
#variant_name(#path::Origin<#runtime, #path::#inst>),
},
Some(inst) => quote! {
#attr
#[codec(index = #index)]
#variant_name(#path::Origin<#path::#inst>),
},
None if part_is_generic => quote! {
#attr
#[codec(index = #index)]
#variant_name(#path::Origin<#runtime>),
},
None => quote! {
#attr
#[codec(index = #index)]
#variant_name(#path::Origin),
},
}
}
fn expand_origin_pallet_conversions(
_scrate: &TokenStream,
runtime: &Ident,
pallet: &Pallet,
instance: Option<&Ident>,
generics: &Generics,
) -> TokenStream {
let path = &pallet.path;
let variant_name = &pallet.name;
let part_is_generic = !generics.params.is_empty();
let pezpallet_origin = match instance {
Some(inst) if part_is_generic => quote!(#path::Origin<#runtime, #path::#inst>),
Some(inst) => quote!(#path::Origin<#path::#inst>),
None if part_is_generic => quote!(#path::Origin<#runtime>),
None => quote!(#path::Origin),
};
let doc_string = get_intra_doc_string(" Convert to runtime origin using", &path.module_name());
let attr = pallet.get_attributes();
quote! {
#attr
impl From<#pezpallet_origin> for OriginCaller {
fn from(x: #pezpallet_origin) -> Self {
OriginCaller::#variant_name(x)
}
}
#attr
impl From<#pezpallet_origin> for RuntimeOrigin {
#[doc = #doc_string]
fn from(x: #pezpallet_origin) -> Self {
let x: OriginCaller = x.into();
x.into()
}
}
#attr
impl From<RuntimeOrigin> for core::result::Result<#pezpallet_origin, RuntimeOrigin> {
/// NOTE: converting to pallet origin loses the origin filter information.
fn from(val: RuntimeOrigin) -> Self {
if let OriginCaller::#variant_name(l) = val.caller {
Ok(l)
} else {
Err(val)
}
}
}
#attr
impl TryFrom<OriginCaller> for #pezpallet_origin {
type Error = OriginCaller;
fn try_from(
x: OriginCaller,
) -> core::result::Result<#pezpallet_origin, OriginCaller> {
if let OriginCaller::#variant_name(l) = x {
Ok(l)
} else {
Err(x)
}
}
}
#attr
impl<'a> TryFrom<&'a OriginCaller> for &'a #pezpallet_origin {
type Error = ();
fn try_from(
x: &'a OriginCaller,
) -> core::result::Result<&'a #pezpallet_origin, ()> {
if let OriginCaller::#variant_name(l) = x {
Ok(&l)
} else {
Err(())
}
}
}
#attr
impl<'a> TryFrom<&'a RuntimeOrigin> for &'a #pezpallet_origin {
type Error = ();
fn try_from(
x: &'a RuntimeOrigin,
) -> core::result::Result<&'a #pezpallet_origin, ()> {
if let OriginCaller::#variant_name(l) = &x.caller {
Ok(&l)
} else {
Err(())
}
}
}
}
}
// Get the actual documentation using the doc information and system path name
fn get_intra_doc_string(doc_info: &str, system_path_name: &String) -> String {
format!(" {} [`{}::Config::BaseCallFilter`].", doc_info, system_path_name)
}
@@ -0,0 +1,267 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License
use crate::construct_runtime::Pallet;
use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens};
use syn::{Generics, Ident};
/// Represents the types supported for creating an outer enum.
#[derive(Clone, Copy, PartialEq)]
pub enum OuterEnumType {
/// Collects the Event enums from all pallets.
Event,
/// Collects the Error enums from all pallets.
Error,
}
impl OuterEnumType {
/// The name of the structure this enum represents.
fn struct_name(&self) -> &str {
match self {
OuterEnumType::Event => "RuntimeEvent",
OuterEnumType::Error => "RuntimeError",
}
}
/// The name of the variant (ie `Event` or `Error`).
fn variant_name(&self) -> &str {
match self {
OuterEnumType::Event => "Event",
OuterEnumType::Error => "Error",
}
}
}
impl ToTokens for OuterEnumType {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
OuterEnumType::Event => quote!(Event).to_tokens(tokens),
OuterEnumType::Error => quote!(Error).to_tokens(tokens),
}
}
}
/// Create an outer enum that encapsulates all pallets as variants.
///
/// Each variant represents a pallet and contains the corresponding type declared with either:
/// - #[pallet::event] for the [`OuterEnumType::Event`] variant
/// - #[pallet::error] for the [`OuterEnumType::Error`] variant
///
/// The name of the outer enum is prefixed with Runtime, resulting in names like RuntimeEvent
/// or RuntimeError.
///
/// This structure facilitates the decoding process by leveraging the metadata.
///
/// # Example
///
/// The code generate looks like the following for [`OuterEnumType::Event`].
///
/// ```ignore
/// enum RuntimeEvent {
/// #[codec(index = 0)]
/// System(pezpallet_system::Event),
///
/// #[codec(index = 5)]
/// Balances(pezpallet_system::Event),
/// }
/// ```
///
/// Notice that the pallet index is preserved using the `#[codec(index = ..)]` attribute.
pub fn expand_outer_enum(
runtime: &Ident,
pezpallet_decls: &[Pallet],
scrate: &TokenStream,
enum_ty: OuterEnumType,
) -> syn::Result<TokenStream> {
// Stores all pallet variants.
let mut enum_variants = TokenStream::new();
// Generates the enum conversion between the `Runtime` outer enum and the pallet's enum.
let mut enum_conversions = TokenStream::new();
// Specific for events to query via `is_event_part_defined!`.
let mut query_enum_part_macros = Vec::new();
let enum_name_str = enum_ty.variant_name();
let enum_name_ident = Ident::new(enum_ty.struct_name(), Span::call_site());
for pezpallet_decl in pezpallet_decls {
let Some(pezpallet_entry) = pezpallet_decl.find_part(enum_name_str) else { continue };
let path = &pezpallet_decl.path;
let pezpallet_name = &pezpallet_decl.name;
let index = pezpallet_decl.index;
let instance = pezpallet_decl.instance.as_ref();
let generics = &pezpallet_entry.generics;
if instance.is_some() && generics.params.is_empty() {
let msg = format!(
"Instantiable pallet with no generic `{}` cannot \
be constructed: pallet `{}` must have generic `{}`",
enum_name_str, pezpallet_name, enum_name_str,
);
return Err(syn::Error::new(pezpallet_name.span(), msg));
}
let part_is_generic = !generics.params.is_empty();
let pezpallet_enum = match (instance, part_is_generic) {
(Some(inst), true) => quote!(#path::#enum_ty::<#runtime, #path::#inst>),
(Some(inst), false) => quote!(#path::#enum_ty::<#path::#inst>),
(None, true) => quote!(#path::#enum_ty::<#runtime>),
(None, false) => quote!(#path::#enum_ty),
};
enum_variants.extend(expand_enum_variant(
runtime,
pezpallet_decl,
index,
instance,
generics,
enum_ty,
));
enum_conversions.extend(expand_enum_conversion(
pezpallet_decl,
&pezpallet_enum,
&enum_name_ident,
));
if enum_ty == OuterEnumType::Event {
query_enum_part_macros.push(quote! {
#path::__bizinikiwi_event_check::is_event_part_defined!(#pezpallet_name);
});
}
}
// Derives specific for the event.
let event_custom_derives =
if enum_ty == OuterEnumType::Event { quote!(Clone, PartialEq, Eq,) } else { quote!() };
// Implementation specific for errors.
let error_custom_impl = generate_error_impl(scrate, enum_ty);
Ok(quote! {
#( #query_enum_part_macros )*
#[derive(
#event_custom_derives
#scrate::__private::codec::Encode,
#scrate::__private::codec::Decode,
#scrate::__private::codec::DecodeWithMemTracking,
#scrate::__private::scale_info::TypeInfo,
#scrate::__private::Debug,
)]
#[allow(non_camel_case_types)]
pub enum #enum_name_ident {
#enum_variants
}
#enum_conversions
#error_custom_impl
})
}
fn expand_enum_variant(
runtime: &Ident,
pallet: &Pallet,
index: u8,
instance: Option<&Ident>,
generics: &Generics,
enum_ty: OuterEnumType,
) -> TokenStream {
let path = &pallet.path;
let variant_name = &pallet.name;
let part_is_generic = !generics.params.is_empty();
let attr = pallet.get_attributes();
match instance {
Some(inst) if part_is_generic => quote! {
#attr
#[codec(index = #index)]
#variant_name(#path::#enum_ty<#runtime, #path::#inst>),
},
Some(inst) => quote! {
#attr
#[codec(index = #index)]
#variant_name(#path::#enum_ty<#path::#inst>),
},
None if part_is_generic => quote! {
#attr
#[codec(index = #index)]
#variant_name(#path::#enum_ty<#runtime>),
},
None => quote! {
#attr
#[codec(index = #index)]
#variant_name(#path::#enum_ty),
},
}
}
fn expand_enum_conversion(
pallet: &Pallet,
pezpallet_enum: &TokenStream,
enum_name_ident: &Ident,
) -> TokenStream {
let variant_name = &pallet.name;
let attr = pallet.get_attributes();
quote! {
#attr
#[allow(deprecated)]
impl From<#pezpallet_enum> for #enum_name_ident {
fn from(x: #pezpallet_enum) -> Self {
#enum_name_ident
::#variant_name(x)
}
}
#attr
#[allow(deprecated)]
impl TryInto<#pezpallet_enum> for #enum_name_ident {
type Error = ();
fn try_into(self) -> ::core::result::Result<#pezpallet_enum, Self::Error> {
match self {
Self::#variant_name(evt) => Ok(evt),
_ => Err(()),
}
}
}
}
}
fn generate_error_impl(scrate: &TokenStream, enum_ty: OuterEnumType) -> TokenStream {
// Implementation is specific to `Error`s.
if enum_ty == OuterEnumType::Event {
return quote! {};
}
let enum_name_ident = Ident::new(enum_ty.struct_name(), Span::call_site());
quote! {
impl #enum_name_ident {
/// Optionally convert the `DispatchError` into the `RuntimeError`.
///
/// Returns `Some` if the error matches the `DispatchError::Module` variant, otherwise `None`.
pub fn from_dispatch_error(err: #scrate::pezsp_runtime::DispatchError) -> Option<Self> {
let #scrate::pezsp_runtime::DispatchError::Module(module_error) = err else { return None };
let bytes = #scrate::__private::codec::Encode::encode(&module_error);
#scrate::__private::codec::Decode::decode(&mut &bytes[..]).ok()
}
}
}
}
@@ -0,0 +1,67 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License
use super::composite_helper;
use crate::construct_runtime::Pallet;
use proc_macro2::TokenStream;
use quote::quote;
pub fn expand_outer_slash_reason(pezpallet_decls: &[Pallet], scrate: &TokenStream) -> TokenStream {
let mut conversion_fns = Vec::new();
let mut slash_reason_variants = Vec::new();
for decl in pezpallet_decls {
if let Some(_) = decl.find_part("SlashReason") {
let variant_name = &decl.name;
let path = &decl.path;
let index = decl.index;
let instance = decl.instance.as_ref();
conversion_fns.push(composite_helper::expand_conversion_fn(
"SlashReason",
path,
instance,
variant_name,
));
slash_reason_variants.push(composite_helper::expand_variant(
"SlashReason",
index,
path,
instance,
variant_name,
));
}
}
quote! {
/// A reason for slashing funds.
#[derive(
Copy, Clone, Eq, PartialEq,
#scrate::__private::codec::Encode,
#scrate::__private::codec::Decode,
#scrate::__private::codec::DecodeWithMemTracking,
#scrate::__private::codec::MaxEncodedLen,
#scrate::__private::scale_info::TypeInfo,
#scrate::__private::RuntimeDebug,
)]
pub enum RuntimeSlashReason {
#( #slash_reason_variants )*
}
#( #conversion_fns )*
}
}
@@ -0,0 +1,157 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License
use crate::construct_runtime::Pallet;
use proc_macro2::{Ident, TokenStream as TokenStream2};
use quote::quote;
/// Expands aggregate `RuntimeTask` enum.
pub fn expand_outer_task(
runtime_name: &Ident,
pezpallet_decls: &[Pallet],
scrate: &TokenStream2,
) -> TokenStream2 {
let mut from_impls = Vec::new();
let mut task_variants = Vec::new();
let mut variant_names = Vec::new();
let mut task_types = Vec::new();
let mut cfg_attrs = Vec::new();
for decl in pezpallet_decls {
if decl.find_part("Task").is_none() {
continue;
}
let variant_name = &decl.name;
let path = &decl.path;
let index = decl.index;
let instance = decl.instance.as_ref().map(|instance| quote!(, #path::#instance));
let task_type = quote!(#path::Task<#runtime_name #instance>);
let attr = decl.get_attributes();
from_impls.push(quote! {
#attr
impl From<#task_type> for RuntimeTask {
fn from(hr: #task_type) -> Self {
RuntimeTask::#variant_name(hr)
}
}
#attr
impl TryInto<#task_type> for RuntimeTask {
type Error = ();
fn try_into(self) -> Result<#task_type, Self::Error> {
match self {
RuntimeTask::#variant_name(hr) => Ok(hr),
_ => Err(()),
}
}
}
});
task_variants.push(quote! {
#attr
#[codec(index = #index)]
#variant_name(#task_type),
});
variant_names.push(quote!(#variant_name));
task_types.push(task_type);
cfg_attrs.push(attr);
}
let prelude = quote!(#scrate::traits::tasks::__private);
const INCOMPLETE_MATCH_QED: &'static str =
"cannot have an instantiated RuntimeTask without some Task variant in the runtime. QED";
let output = quote! {
/// An aggregation of all `Task` enums across all pallets included in the current runtime.
#[derive(
Clone, Eq, PartialEq,
#scrate::__private::codec::Encode,
#scrate::__private::codec::Decode,
#scrate::__private::codec::DecodeWithMemTracking,
#scrate::__private::scale_info::TypeInfo,
#scrate::__private::RuntimeDebug,
)]
pub enum RuntimeTask {
#( #task_variants )*
}
#[automatically_derived]
impl #scrate::traits::Task for RuntimeTask {
type Enumeration = #prelude::IntoIter<RuntimeTask>;
fn is_valid(&self) -> bool {
match self {
#(
#cfg_attrs
RuntimeTask::#variant_names(val) => val.is_valid(),
)*
_ => unreachable!(#INCOMPLETE_MATCH_QED),
}
}
fn run(&self) -> Result<(), #scrate::traits::tasks::__private::DispatchError> {
match self {
#(
#cfg_attrs
RuntimeTask::#variant_names(val) => val.run(),
)*
_ => unreachable!(#INCOMPLETE_MATCH_QED),
}
}
fn weight(&self) -> #scrate::pezpallet_prelude::Weight {
match self {
#(
#cfg_attrs
RuntimeTask::#variant_names(val) => val.weight(),
)*
_ => unreachable!(#INCOMPLETE_MATCH_QED),
}
}
fn task_index(&self) -> u32 {
match self {
#(
#cfg_attrs
RuntimeTask::#variant_names(val) => val.task_index(),
)*
_ => unreachable!(#INCOMPLETE_MATCH_QED),
}
}
fn iter() -> Self::Enumeration {
let mut all_tasks = Vec::new();
#(
#cfg_attrs
all_tasks.extend(<#task_types>::iter().map(RuntimeTask::from).collect::<Vec<_>>());
)*
all_tasks.into_iter()
}
}
#( #from_impls )*
};
output
}
@@ -0,0 +1,81 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License
use crate::construct_runtime::Pallet;
use proc_macro2::TokenStream;
use quote::quote;
use syn::Ident;
pub fn expand_outer_validate_unsigned(
runtime: &Ident,
pezpallet_decls: &[Pallet],
scrate: &TokenStream,
) -> TokenStream {
let mut pezpallet_names = Vec::new();
let mut pezpallet_attrs = Vec::new();
let mut query_validate_unsigned_part_macros = Vec::new();
for pezpallet_decl in pezpallet_decls {
if pezpallet_decl.exists_part("ValidateUnsigned") {
let name = &pezpallet_decl.name;
let path = &pezpallet_decl.path;
let attr = pezpallet_decl.get_attributes();
pezpallet_names.push(name);
pezpallet_attrs.push(attr);
query_validate_unsigned_part_macros.push(quote! {
#path::__bizinikiwi_validate_unsigned_check::is_validate_unsigned_part_defined!(#name);
});
}
}
quote! {
#( #query_validate_unsigned_part_macros )*
impl #scrate::unsigned::ValidateUnsigned for #runtime {
type Call = RuntimeCall;
fn pre_dispatch(call: &Self::Call) -> Result<(), #scrate::unsigned::TransactionValidityError> {
#[allow(unreachable_patterns)]
match call {
#(
#pezpallet_attrs
RuntimeCall::#pezpallet_names(inner_call) => #pezpallet_names::pre_dispatch(inner_call),
)*
// pre-dispatch should not stop inherent extrinsics, validation should prevent
// including arbitrary (non-inherent) extrinsics to blocks.
_ => Ok(()),
}
}
fn validate_unsigned(
#[allow(unused_variables)]
source: #scrate::unsigned::TransactionSource,
call: &Self::Call,
) -> #scrate::unsigned::TransactionValidity {
#[allow(unreachable_patterns)]
match call {
#(
#pezpallet_attrs
RuntimeCall::#pezpallet_names(inner_call) => #pezpallet_names::validate_unsigned(source, inner_call),
)*
_ => #scrate::unsigned::UnknownTransaction::NoUnsignedValidator.into(),
}
}
}
}
}
@@ -0,0 +1,79 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License
use crate::construct_runtime::Pallet;
use proc_macro2::{Ident, Span, TokenStream as TokenStream2};
/// Expands implementation of runtime level `DispatchViewFunction`.
pub fn expand_outer_query(
runtime_name: &Ident,
pezpallet_decls: &[Pallet],
scrate: &TokenStream2,
) -> TokenStream2 {
let runtime_view_function = syn::Ident::new("RuntimeViewFunction", Span::call_site());
let prefix_conditionals = pezpallet_decls.iter().map(|pallet| {
let pezpallet_name = &pallet.name;
let attr = pallet.get_attributes();
quote::quote! {
#attr
if id.prefix == <#pezpallet_name as #scrate::view_functions::ViewFunctionIdPrefix>::prefix() {
return <#pezpallet_name as #scrate::view_functions::DispatchViewFunction>::dispatch_view_function(id, input, output)
}
}
});
quote::quote! {
/// Runtime query type.
#[derive(
Clone, PartialEq, Eq,
#scrate::__private::codec::Encode,
#scrate::__private::codec::Decode,
#scrate::__private::codec::DecodeWithMemTracking,
#scrate::__private::scale_info::TypeInfo,
#scrate::__private::RuntimeDebug,
)]
pub enum #runtime_view_function {}
const _: () = {
impl #scrate::view_functions::DispatchViewFunction for #runtime_view_function {
fn dispatch_view_function<O: #scrate::__private::codec::Output>(
id: & #scrate::view_functions::ViewFunctionId,
input: &mut &[u8],
output: &mut O
) -> Result<(), #scrate::view_functions::ViewFunctionDispatchError>
{
#( #prefix_conditionals )*
Err(#scrate::view_functions::ViewFunctionDispatchError::NotFound(id.clone()))
}
}
impl #runtime_name {
/// Convenience function for view functions dispatching and execution from the runtime API.
pub fn execute_view_function(
id: #scrate::view_functions::ViewFunctionId,
input: #scrate::__private::Vec<::core::primitive::u8>,
) -> Result<#scrate::__private::Vec<::core::primitive::u8>, #scrate::view_functions::ViewFunctionDispatchError>
{
let mut output = #scrate::__private::vec![];
<#runtime_view_function as #scrate::view_functions::DispatchViewFunction>::dispatch_view_function(&id, &mut &input[..], &mut output)?;
Ok(output)
}
}
};
}
}
@@ -0,0 +1,807 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Implementation of `construct_runtime`.
//!
//! `construct_runtime` implementation is recursive and can generate code which will call itself in
//! order to get all the pallet parts for each pallet.
//!
//! Pallets can define their parts:
//! - Implicitly: `System: pezframe_system`
//! - Explicitly: `System: pezframe_system::{Pallet, Call}`
//!
//! The `construct_runtime` transitions from the implicit definition to the explicit one.
//! From the explicit state, Bizinikiwi expands the pallets with additional information
//! that is to be included in the runtime metadata. This expansion makes visible some extra
//! parts of the pallets, mainly the `Error` if defined. The expanded state looks like
//! `System: pezframe_system expanded::{Error} ::{Pallet, Call}` and concatenates the extra expanded
//! parts with the user-provided parts. For example, the `Pallet`, `Call` and `Error` parts are
//! collected.
//!
//! Pallets must provide the `tt_extra_parts` and `tt_default_parts` macros for these transitions.
//! These are automatically implemented by the `#[pallet::pallet]` macro.
//!
//! This macro also generates the following enums for ease of decoding:
//! - `enum RuntimeCall`: This type contains the information needed to decode extrinsics.
//! - `enum RuntimeEvent`: This type contains the information needed to decode events.
//! - `enum RuntimeError`: While this cannot be used directly to decode `pezsp_runtime::DispatchError`
//! from the chain, it contains the information needed to decode the
//! `pezsp_runtime::DispatchError::Module`.
//!
//! # State Transitions
//!
//! ```ignore
//! +----------+
//! | Implicit | -----------+
//! +----------+ |
//! | |
//! v v
//! +----------+ +------------------+
//! | Explicit | --> | ExplicitExpanded |
//! +----------+ +------------------+
//! ```
//!
//! When all pallet parts are implicit, then the `construct_runtime!` macro expands to its final
//! state, the `ExplicitExpanded`. Otherwise, all implicit parts are converted to an explicit
//! expanded part allow the `construct_runtime!` to expand any remaining explicit parts to an
//! explicit expanded part.
//!
//! # Implicit to Explicit
//!
//! The `construct_runtime` macro transforms the implicit declaration of each pallet
//! `System: pezframe_system` to an explicit one `System: pezframe_system::{Pallet, Call}` using the
//! `tt_default_parts` macro.
//!
//! The `tt_default_parts` macro exposes a comma separated list of pallet parts. For example, the
//! `Event` part is exposed only if the pallet implements an event via `#[pallet::event]` macro.
//! The tokens generated by this macro are ` expanded :: { Pallet, Call }` for our example.
//!
//! The `match_and_insert` macro takes in 3 arguments:
//! - target: This is the `TokenStream` that contains the `construct_runtime!` macro.
//! - pattern: The pattern to match against in the target stream.
//! - tokens: The tokens to added after the pattern match.
//!
//! The `construct_runtime` macro uses the `tt_call` to get the default pallet parts via
//! the `tt_default_parts` macro defined by each pallet. The pallet parts are then returned as
//! input to the `match_and_replace` macro.
//! The `match_and_replace` then will modify the the `construct_runtime!` to expand the implicit
//! definition to the explicit one.
//!
//! For example,
//!
//! ```ignore
//! construct_runtime!(
//! //...
//! {
//! System: pezframe_system = 0, // Implicit definition of parts
//! Balances: pezpallet_balances = 1, // Implicit definition of parts
//! }
//! );
//! ```
//! This call has some implicit pallet parts, thus it will expand to:
//! ```ignore
//! pezframe_support::__private::tt_call! {
//! macro = [{ pezpallet_balances::tt_default_parts }]
//! ~~> pezframe_support::match_and_insert! {
//! target = [{
//! pezframe_support::__private::tt_call! {
//! macro = [{ pezframe_system::tt_default_parts }]
//! ~~> pezframe_support::match_and_insert! {
//! target = [{
//! construct_runtime!(
//! //...
//! {
//! System: pezframe_system = 0,
//! Balances: pezpallet_balances = 1,
//! }
//! );
//! }]
//! pattern = [{ System: pezframe_system }]
//! }
//! }
//! }]
//! pattern = [{ Balances: pezpallet_balances }]
//! }
//! }
//! ```
//! `tt_default_parts` must be defined. It returns the pallet parts inside some tokens, and
//! then `tt_call` will pipe the returned pallet parts into the input of `match_and_insert`.
//! Thus `match_and_insert` will initially receive the following inputs:
//! ```ignore
//! pezframe_support::match_and_insert! {
//! target = [{
//! pezframe_support::match_and_insert! {
//! target = [{
//! construct_runtime!(
//! //...
//! {
//! System: pezframe_system = 0,
//! Balances: pezpallet_balances = 1,
//! }
//! )
//! }]
//! pattern = [{ System: pezframe_system }]
//! tokens = [{ ::{Pallet, Call} }]
//! }
//! }]
//! pattern = [{ Balances: pezpallet_balances }]
//! tokens = [{ ::{Pallet, Call} }]
//! }
//! ```
//! After dealing with `pezpallet_balances`, the inner `match_and_insert` will expand to:
//! ```ignore
//! pezframe_support::match_and_insert! {
//! target = [{
//! construct_runtime!(
//! //...
//! {
//! System: pezframe_system = 0, // Implicit definition of parts
//! Balances: pezpallet_balances::{Pallet, Call} = 1, // Explicit definition of parts
//! }
//! )
//! }]
//! pattern = [{ System: pezframe_system }]
//! tokens = [{ ::{Pallet, Call} }]
//! }
//! ```
//!
//! Which will then finally expand to the following:
//! ```ignore
//! construct_runtime!(
//! //...
//! {
//! System: pezframe_system::{Pallet, Call},
//! Balances: pezpallet_balances::{Pallet, Call},
//! }
//! )
//! ```
//!
//! This call has no implicit pallet parts, thus it will expand to the runtime construction:
//! ```ignore
//! pub enum Runtime { ... }
//! pub struct Call { ... }
//! impl Call ...
//! pub enum Origin { ... }
//! ...
//! ```
//!
//! Visualizing the entire flow of `construct_runtime!`, it would look like the following:
//!
//! ```ignore
//! +--------------------+ +---------------------+ +-------------------+
//! | | | (defined in pallet) | | |
//! | construct_runtime! | --> | tt_default_parts! | --> | match_and_insert! |
//! | w/ no pallet parts | | | | |
//! +--------------------+ +---------------------+ +-------------------+
//!
//! +--------------------+
//! | |
//! --> | construct_runtime! |
//! | w/ pallet parts |
//! +--------------------+
//! ```
//!
//! # Explicit to Explicit Expanded
//!
//! Users normally do not care about this transition.
//!
//! Similarly to the previous transition, the macro expansion transforms `System:
//! pezframe_system::{Pallet, Call}` into `System: pezframe_system expanded::{Error} ::{Pallet, Call}`.
//! The `expanded` section adds extra parts that the Bizinikiwi would like to expose for each pallet
//! by default. This is done to expose the appropriate types for metadata construction.
//!
//! This time, instead of calling `tt_default_parts` we are using the `tt_extra_parts` macro.
//! This macro returns the ` :: expanded { Error }` list of additional parts we would like to
//! expose.
pub(crate) mod expand;
pub(crate) mod parse;
use crate::pallet::parse::helper::two128_str;
use cfg_expr::Predicate;
use pezframe_support_procedural_tools::{
generate_access_from_frame_or_crate, generate_crate_access, generate_hidden_includes,
};
use itertools::Itertools;
use parse::{ExplicitRuntimeDeclaration, ImplicitRuntimeDeclaration, Pallet, RuntimeDeclaration};
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use std::{collections::HashSet, str::FromStr};
use syn::{spanned::Spanned, Ident, Result};
/// The fixed name of the system pallet.
const SYSTEM_PALLET_NAME: &str = "System";
/// Implementation of `construct_runtime` macro. Either expand to some code which will call
/// `construct_runtime` again, or expand to the final runtime definition.
pub fn construct_runtime(input: TokenStream) -> TokenStream {
let input_copy = input.clone();
let definition = syn::parse_macro_input!(input as RuntimeDeclaration);
let (check_pallet_number_res, res) = match definition {
RuntimeDeclaration::Implicit(implicit_def) => (
check_pallet_number(input_copy.clone().into(), implicit_def.pallets.len()),
construct_runtime_implicit_to_explicit(input_copy.into(), implicit_def),
),
RuntimeDeclaration::Explicit(explicit_decl) => (
check_pallet_number(input_copy.clone().into(), explicit_decl.pallets.len()),
construct_runtime_explicit_to_explicit_expanded(input_copy.into(), explicit_decl),
),
RuntimeDeclaration::ExplicitExpanded(explicit_decl) => (
check_pallet_number(input_copy.into(), explicit_decl.pallets.len()),
construct_runtime_final_expansion(explicit_decl),
),
};
let res = res.unwrap_or_else(|e| e.to_compile_error());
// We want to provide better error messages to the user and thus, handle the error here
// separately. If there is an error, we print the error and still generate all of the code to
// get in overall less errors for the user.
let res = if let Err(error) = check_pallet_number_res {
let error = error.to_compile_error();
quote! {
#error
#res
}
} else {
res
};
let res = expander::Expander::new("construct_runtime")
.dry(std::env::var("EXPAND_MACROS").is_err())
.verbose(true)
.write_to_out_dir(res)
.expect("Does not fail because of IO in OUT_DIR; qed");
res.into()
}
/// All pallets that have implicit pallet parts (ie `System: pezframe_system`) are
/// expanded with the default parts defined by the pallet's `tt_default_parts` macro.
///
/// This function transforms the [`RuntimeDeclaration::Implicit`] into
/// [`RuntimeDeclaration::Explicit`] that is not yet fully expanded.
///
/// For more details, please refer to the root documentation.
fn construct_runtime_implicit_to_explicit(
input: TokenStream2,
definition: ImplicitRuntimeDeclaration,
) -> Result<TokenStream2> {
let pezframe_support = generate_access_from_frame_or_crate("pezframe-support")?;
let mut expansion = quote::quote!(
#pezframe_support::construct_runtime! { #input }
);
for pallet in definition.pallets.iter().filter(|pallet| pallet.pezpallet_parts.is_none()) {
let pezpallet_path = &pallet.path;
let pezpallet_name = &pallet.name;
let pezpallet_instance = pallet.instance.as_ref().map(|instance| quote::quote!(::<#instance>));
expansion = quote::quote!(
#pezframe_support::__private::tt_call! {
macro = [{ #pezpallet_path::tt_default_parts }]
your_tt_return = [{ #pezframe_support::__private::tt_return }]
~~> #pezframe_support::match_and_insert! {
target = [{ #expansion }]
pattern = [{ #pezpallet_name: #pezpallet_path #pezpallet_instance }]
}
}
);
}
Ok(expansion)
}
/// All pallets that have
/// (I): explicit pallet parts (ie `System: pezframe_system::{Pallet, Call}`) and
/// (II): are not fully expanded (ie do not include the `Error` expansion part)
/// are fully expanded by including the parts from the pallet's `tt_extra_parts` macro.
///
/// This function transforms the [`RuntimeDeclaration::Explicit`] that is not yet fully expanded
/// into [`RuntimeDeclaration::ExplicitExpanded`] fully expanded.
///
/// For more details, please refer to the root documentation.
fn construct_runtime_explicit_to_explicit_expanded(
input: TokenStream2,
definition: ExplicitRuntimeDeclaration,
) -> Result<TokenStream2> {
let pezframe_support = generate_access_from_frame_or_crate("pezframe-support")?;
let mut expansion = quote::quote!(
#pezframe_support::construct_runtime! { #input }
);
for pallet in definition.pallets.iter().filter(|pallet| !pallet.is_expanded) {
let pezpallet_path = &pallet.path;
let pezpallet_name = &pallet.name;
let pezpallet_instance = pallet.instance.as_ref().map(|instance| quote::quote!(::<#instance>));
expansion = quote::quote!(
#pezframe_support::__private::tt_call! {
macro = [{ #pezpallet_path::tt_extra_parts }]
your_tt_return = [{ #pezframe_support::__private::tt_return }]
~~> #pezframe_support::match_and_insert! {
target = [{ #expansion }]
pattern = [{ #pezpallet_name: #pezpallet_path #pezpallet_instance }]
}
}
);
}
Ok(expansion)
}
/// All pallets have explicit definition of parts, this will expand to the runtime declaration.
fn construct_runtime_final_expansion(
definition: ExplicitRuntimeDeclaration,
) -> Result<TokenStream2> {
let ExplicitRuntimeDeclaration { name, pallets, pallets_token, where_section } = definition;
let system_pallet =
pallets.iter().find(|decl| decl.name == SYSTEM_PALLET_NAME).ok_or_else(|| {
syn::Error::new(
pallets_token.span.join(),
"`System` pallet declaration is missing. \
Please add this line: `System: pezframe_system,`",
)
})?;
if !system_pallet.cfg_pattern.is_empty() {
return Err(syn::Error::new(
system_pallet.name.span(),
"`System` pallet declaration is feature gated, please remove any `#[cfg]` attributes",
));
}
let features = pallets
.iter()
.filter_map(|decl| {
(!decl.cfg_pattern.is_empty()).then(|| {
decl.cfg_pattern.iter().flat_map(|attr| {
attr.predicates().filter_map(|pred| match pred {
Predicate::Feature(feat) => Some(feat),
Predicate::Test => Some("test"),
_ => None,
})
})
})
})
.flatten()
.collect::<HashSet<_>>();
let hidden_crate_name = "construct_runtime";
let scrate = generate_crate_access(hidden_crate_name, "pezframe-support");
let scrate_decl = generate_hidden_includes(hidden_crate_name, "pezframe-support");
let pezframe_system = generate_access_from_frame_or_crate("pezframe-system")?;
let block = quote!(<#name as #pezframe_system::Config>::Block);
let unchecked_extrinsic = quote!(<#block as #scrate::pezsp_runtime::traits::Block>::Extrinsic);
let outer_event =
expand::expand_outer_enum(&name, &pallets, &scrate, expand::OuterEnumType::Event)?;
let outer_error =
expand::expand_outer_enum(&name, &pallets, &scrate, expand::OuterEnumType::Error)?;
let outer_origin = expand::expand_outer_origin(&name, system_pallet, &pallets, &scrate)?;
let all_pallets = decl_all_pallets(&name, pallets.iter(), &features);
let pezpallet_to_index = decl_pallet_runtime_setup(&name, &pallets, &scrate);
let dispatch = expand::expand_outer_dispatch(&name, system_pallet, &pallets, &scrate);
let tasks = expand::expand_outer_task(&name, &pallets, &scrate);
let query = expand::expand_outer_query(&name, &pallets, &scrate);
let metadata = expand::expand_runtime_metadata(
&name,
&pallets,
&scrate,
&unchecked_extrinsic,
&system_pallet.path,
);
let outer_config = expand::expand_outer_config(&name, &pallets, &scrate);
let inherent =
expand::expand_outer_inherent(&name, &block, &unchecked_extrinsic, &pallets, &scrate);
let validate_unsigned = expand::expand_outer_validate_unsigned(&name, &pallets, &scrate);
let freeze_reason = expand::expand_outer_freeze_reason(&pallets, &scrate);
let hold_reason = expand::expand_outer_hold_reason(&pallets, &scrate);
let lock_id = expand::expand_outer_lock_id(&pallets, &scrate);
let slash_reason = expand::expand_outer_slash_reason(&pallets, &scrate);
let integrity_test = decl_integrity_test(&scrate);
let static_assertions = decl_static_assertions(&name, &pallets, &scrate);
let warning = where_section.map_or(None, |where_section| {
Some(
proc_macro_warning::Warning::new_deprecated("WhereSection")
.old("use a `where` clause in `construct_runtime`")
.new(
"use `pezframe_system::Config` to set the `Block` type and delete this clause.
It is planned to be removed in December 2023",
)
.help_links(&["https://github.com/pezkuwichain/kurdistan-sdk/issues/51"])
.span(where_section.span)
.build_or_panic(),
)
});
let res = quote!(
#warning
#scrate_decl
// Prevent UncheckedExtrinsic to print unused warning.
const _: () = {
#[allow(unused)]
type __hidden_use_of_unchecked_extrinsic = #unchecked_extrinsic;
};
#[derive(
Clone, Copy, PartialEq, Eq, #scrate::pezsp_runtime::RuntimeDebug,
#scrate::__private::scale_info::TypeInfo
)]
pub struct #name;
impl #scrate::pezsp_runtime::traits::GetRuntimeBlockType for #name {
type RuntimeBlock = #block;
}
// Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata.
// The function is implemented by calling `impl_runtime_apis!`.
//
// However, the `construct_runtime!` may be called without calling `impl_runtime_apis!`.
// Rely on the `Deref` trait to differentiate between a runtime that implements
// APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro construct_runtime!).
//
// Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function.
// `InternalConstructRuntime` is implemented by the `construct_runtime!` for Runtime references (`& Runtime`),
// while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`).
//
// Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!`
// when both macros are called; and will resolve an empty `runtime_metadata` when only the `construct_runtime!`
// is called.
#[doc(hidden)]
trait InternalConstructRuntime {
#[inline(always)]
fn runtime_metadata(&self) -> #scrate::__private::Vec<#scrate::__private::metadata_ir::RuntimeApiMetadataIR> {
Default::default()
}
}
#[doc(hidden)]
impl InternalConstructRuntime for &#name {}
use #scrate::__private::metadata_ir::InternalImplRuntimeApis;
#outer_event
#outer_error
#outer_origin
#all_pallets
#pezpallet_to_index
#dispatch
#tasks
#query
#metadata
#outer_config
#inherent
#validate_unsigned
#freeze_reason
#hold_reason
#lock_id
#slash_reason
#integrity_test
#static_assertions
);
Ok(res)
}
pub(crate) fn decl_all_pallets<'a>(
runtime: &'a Ident,
pezpallet_declarations: impl Iterator<Item = &'a Pallet>,
features: &HashSet<&str>,
) -> TokenStream2 {
let mut types = TokenStream2::new();
// Every feature set to the pallet names that should be included by this feature set.
let mut features_to_names = features
.iter()
.map(|f| *f)
.powerset()
.map(|feat| (HashSet::from_iter(feat), Vec::new()))
.collect::<Vec<(HashSet<_>, Vec<_>)>>();
for pezpallet_declaration in pezpallet_declarations {
let type_name = &pezpallet_declaration.name;
let pallet = &pezpallet_declaration.path;
let docs = &pezpallet_declaration.docs;
let mut generics = vec![quote!(#runtime)];
generics.extend(pezpallet_declaration.instance.iter().map(|name| quote!(#pallet::#name)));
let mut attrs = Vec::new();
for cfg in &pezpallet_declaration.cfg_pattern {
let feat = format!("#[cfg({})]\n", cfg.original());
attrs.extend(TokenStream2::from_str(&feat).expect("was parsed successfully; qed"));
}
let type_decl = quote!(
#( #[doc = #docs] )*
#(#attrs)*
pub type #type_name = #pallet::Pallet <#(#generics),*>;
);
types.extend(type_decl);
if pezpallet_declaration.cfg_pattern.is_empty() {
for (_, names) in features_to_names.iter_mut() {
names.push(&pezpallet_declaration.name);
}
} else {
for (feature_set, names) in &mut features_to_names {
// Rust tidbit: if we have multiple `#[cfg]` feature on the same item, then the
// predicates listed in all `#[cfg]` attributes are effectively joined by `and()`,
// meaning that all of them must match in order to activate the item
let is_feature_active = pezpallet_declaration.cfg_pattern.iter().all(|expr| {
expr.eval(|pred| match pred {
Predicate::Feature(f) => feature_set.contains(f),
Predicate::Test => feature_set.contains(&"test"),
_ => false,
})
});
if is_feature_active {
names.push(&pezpallet_declaration.name);
}
}
}
}
// All possible features. This will be used below for the empty feature set.
let mut all_features = features_to_names
.iter()
.flat_map(|f| f.0.iter().cloned())
.collect::<HashSet<_>>();
let attribute_to_names = features_to_names
.into_iter()
.map(|(mut features, names)| {
// If this is the empty feature set, it needs to be changed to negate all available
// features. So, we ensure that there is some type declared when all features are not
// enabled.
if features.is_empty() {
let test_cfg = all_features.remove("test").then_some(quote!(test)).into_iter();
let features = all_features.iter();
let attr = quote!(#[cfg(all( #(not(#test_cfg)),* #(not(feature = #features)),* ))]);
(attr, names)
} else {
let test_cfg = features.remove("test").then_some(quote!(test)).into_iter();
let disabled_features = all_features.difference(&features);
let features = features.iter();
let attr = quote!(#[cfg(all( #(#test_cfg,)* #(feature = #features,)* #(not(feature = #disabled_features)),* ))]);
(attr, names)
}
})
.collect::<Vec<_>>();
let all_pallets_without_system = attribute_to_names.iter().map(|(attr, names)| {
let names = names.iter().filter(|n| **n != SYSTEM_PALLET_NAME);
quote! {
#attr
/// All pallets included in the runtime as a nested tuple of types.
/// Excludes the System pallet.
pub type AllPalletsWithoutSystem = ( #(#names,)* );
}
});
let all_pallets_with_system = attribute_to_names.iter().map(|(attr, names)| {
quote! {
#attr
/// All pallets included in the runtime as a nested tuple of types.
pub type AllPalletsWithSystem = ( #(#names,)* );
}
});
quote!(
#types
#( #all_pallets_with_system )*
#( #all_pallets_without_system )*
)
}
pub(crate) fn decl_pallet_runtime_setup(
runtime: &Ident,
pezpallet_declarations: &[Pallet],
scrate: &TokenStream2,
) -> TokenStream2 {
let names = pezpallet_declarations.iter().map(|d| &d.name).collect::<Vec<_>>();
let name_strings = pezpallet_declarations.iter().map(|d| d.name.to_string());
let name_hashes = pezpallet_declarations.iter().map(|d| two128_str(&d.name.to_string()));
let module_names = pezpallet_declarations.iter().map(|d| d.path.module_name());
let indices = pezpallet_declarations.iter().map(|pallet| pallet.index as usize);
let pezpallet_structs = pezpallet_declarations
.iter()
.map(|pallet| {
let path = &pallet.path;
match pallet.instance.as_ref() {
Some(inst) => quote!(#path::Pallet<#runtime, #path::#inst>),
None => quote!(#path::Pallet<#runtime>),
}
})
.collect::<Vec<_>>();
let pezpallet_attrs = pezpallet_declarations
.iter()
.map(|pallet| pallet.get_attributes())
.collect::<Vec<_>>();
quote!(
/// Provides an implementation of `PalletInfo` to provide information
/// about the pallet setup in the runtime.
pub struct PalletInfo;
impl #scrate::traits::PalletInfo for PalletInfo {
fn index<P: 'static>() -> Option<usize> {
let type_id = core::any::TypeId::of::<P>();
#(
#pezpallet_attrs
if type_id == core::any::TypeId::of::<#names>() {
return Some(#indices)
}
)*
None
}
fn name<P: 'static>() -> Option<&'static str> {
let type_id = core::any::TypeId::of::<P>();
#(
#pezpallet_attrs
if type_id == core::any::TypeId::of::<#names>() {
return Some(#name_strings)
}
)*
None
}
fn name_hash<P: 'static>() -> Option<[u8; 16]> {
let type_id = core::any::TypeId::of::<P>();
#(
#pezpallet_attrs
if type_id == core::any::TypeId::of::<#names>() {
return Some(#name_hashes)
}
)*
None
}
fn module_name<P: 'static>() -> Option<&'static str> {
let type_id = core::any::TypeId::of::<P>();
#(
#pezpallet_attrs
if type_id == core::any::TypeId::of::<#names>() {
return Some(#module_names)
}
)*
None
}
fn crate_version<P: 'static>() -> Option<#scrate::traits::CrateVersion> {
let type_id = core::any::TypeId::of::<P>();
#(
#pezpallet_attrs
if type_id == core::any::TypeId::of::<#names>() {
return Some(
<#pezpallet_structs as #scrate::traits::PalletInfoAccess>::crate_version()
)
}
)*
None
}
}
)
}
pub(crate) fn decl_integrity_test(scrate: &TokenStream2) -> TokenStream2 {
quote!(
#[cfg(test)]
mod __construct_runtime_integrity_test {
use super::*;
#[test]
pub fn runtime_integrity_tests() {
#scrate::__private::pezsp_tracing::try_init_simple();
<AllPalletsWithSystem as #scrate::traits::IntegrityTest>::integrity_test();
}
}
)
}
pub(crate) fn decl_static_assertions(
runtime: &Ident,
pezpallet_decls: &[Pallet],
scrate: &TokenStream2,
) -> TokenStream2 {
let error_encoded_size_check = pezpallet_decls.iter().map(|decl| {
let path = &decl.path;
let assert_message = format!(
"The maximum encoded size of the error type in the `{}` pallet exceeds \
`MAX_MODULE_ERROR_ENCODED_SIZE`",
decl.name,
);
quote! {
#[allow(deprecated)]
#scrate::__private::tt_call! {
macro = [{ #path::tt_error_token }]
your_tt_return = [{ #scrate::__private::tt_return }]
~~> #scrate::assert_error_encoded_size! {
path = [{ #path }]
runtime = [{ #runtime }]
assert_message = [{ #assert_message }]
}
}
}
});
quote! {
#(#error_encoded_size_check)*
}
}
pub(crate) fn check_pallet_number(input: TokenStream2, pezpallet_num: usize) -> Result<()> {
let max_pallet_num = {
if cfg!(feature = "tuples-96") {
96
} else if cfg!(feature = "tuples-128") {
128
} else {
64
}
};
if pezpallet_num > max_pallet_num {
let no_feature = max_pallet_num == 128;
return Err(syn::Error::new(
input.span(),
format!(
"{} To increase this limit, enable the tuples-{} feature of [pezframe_support]. {}",
"The number of pallets exceeds the maximum number of tuple elements.",
max_pallet_num + 32,
if no_feature {
"If the feature does not exist - it needs to be implemented."
} else {
""
},
),
));
}
Ok(())
}
@@ -0,0 +1,787 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use core::str::FromStr;
use pezframe_support_procedural_tools::syn_ext as ext;
use proc_macro2::{Span, TokenStream};
use quote::ToTokens;
use std::collections::{HashMap, HashSet};
use syn::{
ext::IdentExt,
parse::{Parse, ParseStream},
punctuated::Punctuated,
spanned::Spanned,
token, Attribute, Error, Ident, Path, Result, Token,
};
mod keyword {
syn::custom_keyword!(Block);
syn::custom_keyword!(NodeBlock);
syn::custom_keyword!(UncheckedExtrinsic);
syn::custom_keyword!(Pallet);
syn::custom_keyword!(Call);
syn::custom_keyword!(Storage);
syn::custom_keyword!(Event);
syn::custom_keyword!(Error);
syn::custom_keyword!(Config);
syn::custom_keyword!(Origin);
syn::custom_keyword!(Inherent);
syn::custom_keyword!(ValidateUnsigned);
syn::custom_keyword!(FreezeReason);
syn::custom_keyword!(HoldReason);
syn::custom_keyword!(Task);
syn::custom_keyword!(LockId);
syn::custom_keyword!(SlashReason);
syn::custom_keyword!(exclude_parts);
syn::custom_keyword!(use_parts);
syn::custom_keyword!(expanded);
}
/// Declaration of a runtime.
///
/// Pallet declare their part either explicitly or implicitly (using no part declaration)
/// If all pallet have explicit parts then the runtime declaration is explicit, otherwise it is
/// implicit.
#[derive(Debug)]
pub enum RuntimeDeclaration {
Implicit(ImplicitRuntimeDeclaration),
Explicit(ExplicitRuntimeDeclaration),
ExplicitExpanded(ExplicitRuntimeDeclaration),
}
/// Declaration of a runtime with some pallet with implicit declaration of parts.
#[derive(Debug)]
pub struct ImplicitRuntimeDeclaration {
pub pallets: Vec<PalletDeclaration>,
}
/// Declaration of a runtime with all pallet having explicit declaration of parts.
#[derive(Debug)]
pub struct ExplicitRuntimeDeclaration {
pub name: Ident,
pub where_section: Option<WhereSection>,
pub pallets: Vec<Pallet>,
pub pallets_token: token::Brace,
}
impl Parse for RuntimeDeclaration {
fn parse(input: ParseStream) -> Result<Self> {
input.parse::<Token![pub]>()?;
// Support either `enum` or `struct`.
if input.peek(Token![struct]) {
input.parse::<Token![struct]>()?;
} else {
input.parse::<Token![enum]>()?;
}
let name = input.parse::<syn::Ident>()?;
let where_section = if input.peek(token::Where) { Some(input.parse()?) } else { None };
let pallets =
input.parse::<ext::Braces<ext::Punctuated<PalletDeclaration, Token![,]>>>()?;
let pallets_token = pallets.token;
match convert_pallets(pallets.content.inner.into_iter().collect())? {
PalletsConversion::Implicit(pallets) =>
Ok(RuntimeDeclaration::Implicit(ImplicitRuntimeDeclaration { pallets })),
PalletsConversion::Explicit(pallets) =>
Ok(RuntimeDeclaration::Explicit(ExplicitRuntimeDeclaration {
name,
where_section,
pallets,
pallets_token,
})),
PalletsConversion::ExplicitExpanded(pallets) =>
Ok(RuntimeDeclaration::ExplicitExpanded(ExplicitRuntimeDeclaration {
name,
where_section,
pallets,
pallets_token,
})),
}
}
}
#[derive(Debug)]
pub struct WhereSection {
pub span: Span,
}
impl Parse for WhereSection {
fn parse(input: ParseStream) -> Result<Self> {
input.parse::<token::Where>()?;
let mut definitions = Vec::new();
while !input.peek(token::Brace) {
let definition: WhereDefinition = input.parse()?;
definitions.push(definition);
if !input.peek(Token![,]) {
if !input.peek(token::Brace) {
return Err(input.error("Expected `,` or `{`"));
}
break;
}
input.parse::<Token![,]>()?;
}
remove_kind(input, WhereKind::Block, &mut definitions)?;
remove_kind(input, WhereKind::NodeBlock, &mut definitions)?;
remove_kind(input, WhereKind::UncheckedExtrinsic, &mut definitions)?;
if let Some(WhereDefinition { ref kind_span, ref kind, .. }) = definitions.first() {
let msg = format!(
"`{:?}` was declared above. Please use exactly one declaration for `{:?}`.",
kind, kind
);
return Err(Error::new(*kind_span, msg));
}
Ok(Self { span: input.span() })
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub enum WhereKind {
Block,
NodeBlock,
UncheckedExtrinsic,
}
#[derive(Debug)]
pub struct WhereDefinition {
pub kind_span: Span,
pub kind: WhereKind,
}
impl Parse for WhereDefinition {
fn parse(input: ParseStream) -> Result<Self> {
let lookahead = input.lookahead1();
let (kind_span, kind) = if lookahead.peek(keyword::Block) {
(input.parse::<keyword::Block>()?.span(), WhereKind::Block)
} else if lookahead.peek(keyword::NodeBlock) {
(input.parse::<keyword::NodeBlock>()?.span(), WhereKind::NodeBlock)
} else if lookahead.peek(keyword::UncheckedExtrinsic) {
(input.parse::<keyword::UncheckedExtrinsic>()?.span(), WhereKind::UncheckedExtrinsic)
} else {
return Err(lookahead.error());
};
let _: Token![=] = input.parse()?;
let _: syn::TypePath = input.parse()?;
Ok(Self { kind_span, kind })
}
}
/// The declaration of a pallet.
#[derive(Debug, Clone)]
pub struct PalletDeclaration {
/// Is this pallet fully expanded?
pub is_expanded: bool,
/// The name of the pallet, e.g.`System` in `System: pezframe_system`.
pub name: Ident,
/// Optional attributes tagged right above a pallet declaration.
pub attrs: Vec<Attribute>,
/// Optional fixed index, e.g. `MyPallet ... = 3,`.
pub index: Option<u8>,
/// The path of the pallet, e.g. `pezframe_system` in `System: pezframe_system`.
pub path: PalletPath,
/// The instance of the pallet, e.g. `Instance1` in `Council: pezpallet_collective::<Instance1>`.
pub instance: Option<Ident>,
/// The declared pallet parts,
/// e.g. `Some([Pallet, Call])` for `System: system::{Pallet, Call}`
/// or `None` for `System: system`.
pub pezpallet_parts: Option<Vec<PalletPart>>,
/// The specified parts, either use_parts or exclude_parts.
pub specified_parts: SpecifiedParts,
}
/// The possible declaration of pallet parts to use.
#[derive(Debug, Clone)]
pub enum SpecifiedParts {
/// Use all the pallet parts except those specified.
Exclude(Vec<PalletPartNoGeneric>),
/// Use only the specified pallet parts.
Use(Vec<PalletPartNoGeneric>),
/// Use the all the pallet parts.
All,
}
impl Parse for PalletDeclaration {
fn parse(input: ParseStream) -> Result<Self> {
let attrs = input.call(Attribute::parse_outer)?;
let name = input.parse()?;
let _: Token![:] = input.parse()?;
let path = input.parse()?;
// Parse for instance.
let instance = if input.peek(Token![::]) && input.peek3(Token![<]) {
let _: Token![::] = input.parse()?;
let _: Token![<] = input.parse()?;
let res = Some(input.parse()?);
let _: Token![>] = input.parse()?;
res
} else if !(input.peek(Token![::]) && input.peek3(token::Brace)) &&
!input.peek(keyword::expanded) &&
!input.peek(keyword::exclude_parts) &&
!input.peek(keyword::use_parts) &&
!input.peek(Token![=]) &&
!input.peek(Token![,]) &&
!input.is_empty()
{
return Err(input.error(
"Unexpected tokens, expected one of `::$ident` `::{`, `exclude_parts`, `use_parts`, `=`, `,`",
));
} else {
None
};
// Check if the pallet is fully expanded.
let (is_expanded, extra_parts) = if input.peek(keyword::expanded) {
let _: keyword::expanded = input.parse()?;
let _: Token![::] = input.parse()?;
(true, parse_pallet_parts(input)?)
} else {
(false, vec![])
};
// Parse for explicit parts
let pezpallet_parts = if input.peek(Token![::]) && input.peek3(token::Brace) {
let _: Token![::] = input.parse()?;
let mut parts = parse_pallet_parts(input)?;
parts.extend(extra_parts.into_iter());
Some(parts)
} else if !input.peek(keyword::exclude_parts) &&
!input.peek(keyword::use_parts) &&
!input.peek(Token![=]) &&
!input.peek(Token![,]) &&
!input.is_empty()
{
return Err(input.error(
"Unexpected tokens, expected one of `::{`, `exclude_parts`, `use_parts`, `=`, `,`",
));
} else {
is_expanded.then_some(extra_parts)
};
// Parse for specified parts
let specified_parts = if input.peek(keyword::exclude_parts) {
let _: keyword::exclude_parts = input.parse()?;
SpecifiedParts::Exclude(parse_pallet_parts_no_generic(input)?)
} else if input.peek(keyword::use_parts) {
let _: keyword::use_parts = input.parse()?;
SpecifiedParts::Use(parse_pallet_parts_no_generic(input)?)
} else if !input.peek(Token![=]) && !input.peek(Token![,]) && !input.is_empty() {
return Err(input.error("Unexpected tokens, expected one of `exclude_parts`, `=`, `,`"));
} else {
SpecifiedParts::All
};
// Parse for pallet index
let index = if input.peek(Token![=]) {
input.parse::<Token![=]>()?;
let index = input.parse::<syn::LitInt>()?;
let index = index.base10_parse::<u8>()?;
Some(index)
} else if !input.peek(Token![,]) && !input.is_empty() {
return Err(input.error("Unexpected tokens, expected one of `=`, `,`"));
} else {
None
};
Ok(Self { is_expanded, attrs, name, path, instance, pezpallet_parts, specified_parts, index })
}
}
/// A struct representing a path to a pallet. `PalletPath` is almost identical to the standard
/// Rust path with a few restrictions:
/// - No leading colons allowed
/// - Path segments can only consist of identifiers separated by colons
#[derive(Debug, Clone)]
pub struct PalletPath {
pub inner: Path,
}
impl PalletPath {
pub fn module_name(&self) -> String {
self.inner.segments.iter().fold(String::new(), |mut acc, segment| {
if !acc.is_empty() {
acc.push_str("::");
}
acc.push_str(&segment.ident.to_string());
acc
})
}
}
impl Parse for PalletPath {
fn parse(input: ParseStream) -> Result<Self> {
let mut res =
PalletPath { inner: Path { leading_colon: None, segments: Punctuated::new() } };
let lookahead = input.lookahead1();
if lookahead.peek(Token![crate]) ||
lookahead.peek(Token![self]) ||
lookahead.peek(Token![super]) ||
lookahead.peek(Ident)
{
let ident = input.call(Ident::parse_any)?;
res.inner.segments.push(ident.into());
} else {
return Err(lookahead.error());
}
while input.peek(Token![::]) && input.peek3(Ident) {
input.parse::<Token![::]>()?;
let ident = input.parse::<Ident>()?;
res.inner.segments.push(ident.into());
}
Ok(res)
}
}
impl quote::ToTokens for PalletPath {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.inner.to_tokens(tokens);
}
}
/// Parse [`PalletPart`]'s from a braces enclosed list that is split by commas, e.g.
///
/// `{ Call, Event }`
fn parse_pallet_parts(input: ParseStream) -> Result<Vec<PalletPart>> {
let pezpallet_parts: ext::Braces<ext::Punctuated<PalletPart, Token![,]>> = input.parse()?;
let mut resolved = HashSet::new();
for part in pezpallet_parts.content.inner.iter() {
if !resolved.insert(part.name()) {
let msg = format!(
"`{}` was already declared before. Please remove the duplicate declaration",
part.name(),
);
return Err(Error::new(part.keyword.span(), msg));
}
}
Ok(pezpallet_parts.content.inner.into_iter().collect())
}
#[derive(Debug, Clone)]
pub enum PalletPartKeyword {
Pallet(keyword::Pallet),
Call(keyword::Call),
Storage(keyword::Storage),
Event(keyword::Event),
Error(keyword::Error),
Config(keyword::Config),
Origin(keyword::Origin),
Inherent(keyword::Inherent),
ValidateUnsigned(keyword::ValidateUnsigned),
FreezeReason(keyword::FreezeReason),
HoldReason(keyword::HoldReason),
Task(keyword::Task),
LockId(keyword::LockId),
SlashReason(keyword::SlashReason),
}
impl Parse for PalletPartKeyword {
fn parse(input: ParseStream) -> Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(keyword::Pallet) {
Ok(Self::Pallet(input.parse()?))
} else if lookahead.peek(keyword::Call) {
Ok(Self::Call(input.parse()?))
} else if lookahead.peek(keyword::Storage) {
Ok(Self::Storage(input.parse()?))
} else if lookahead.peek(keyword::Event) {
Ok(Self::Event(input.parse()?))
} else if lookahead.peek(keyword::Error) {
Ok(Self::Error(input.parse()?))
} else if lookahead.peek(keyword::Config) {
Ok(Self::Config(input.parse()?))
} else if lookahead.peek(keyword::Origin) {
Ok(Self::Origin(input.parse()?))
} else if lookahead.peek(keyword::Inherent) {
Ok(Self::Inherent(input.parse()?))
} else if lookahead.peek(keyword::ValidateUnsigned) {
Ok(Self::ValidateUnsigned(input.parse()?))
} else if lookahead.peek(keyword::FreezeReason) {
Ok(Self::FreezeReason(input.parse()?))
} else if lookahead.peek(keyword::HoldReason) {
Ok(Self::HoldReason(input.parse()?))
} else if lookahead.peek(keyword::Task) {
Ok(Self::Task(input.parse()?))
} else if lookahead.peek(keyword::LockId) {
Ok(Self::LockId(input.parse()?))
} else if lookahead.peek(keyword::SlashReason) {
Ok(Self::SlashReason(input.parse()?))
} else {
Err(lookahead.error())
}
}
}
impl PalletPartKeyword {
/// Returns the name of `Self`.
fn name(&self) -> &'static str {
match self {
Self::Pallet(_) => "Pallet",
Self::Call(_) => "Call",
Self::Storage(_) => "Storage",
Self::Event(_) => "Event",
Self::Error(_) => "Error",
Self::Config(_) => "Config",
Self::Origin(_) => "Origin",
Self::Inherent(_) => "Inherent",
Self::ValidateUnsigned(_) => "ValidateUnsigned",
Self::FreezeReason(_) => "FreezeReason",
Self::HoldReason(_) => "HoldReason",
Self::Task(_) => "Task",
Self::LockId(_) => "LockId",
Self::SlashReason(_) => "SlashReason",
}
}
/// Returns `true` if this pallet part is allowed to have generic arguments.
fn allows_generic(&self) -> bool {
Self::all_generic_arg().iter().any(|n| *n == self.name())
}
/// Returns the names of all pallet parts that allow to have a generic argument.
fn all_generic_arg() -> &'static [&'static str] {
&["Event", "Error", "Origin", "Config", "Task"]
}
}
impl ToTokens for PalletPartKeyword {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
Self::Pallet(inner) => inner.to_tokens(tokens),
Self::Call(inner) => inner.to_tokens(tokens),
Self::Storage(inner) => inner.to_tokens(tokens),
Self::Event(inner) => inner.to_tokens(tokens),
Self::Error(inner) => inner.to_tokens(tokens),
Self::Config(inner) => inner.to_tokens(tokens),
Self::Origin(inner) => inner.to_tokens(tokens),
Self::Inherent(inner) => inner.to_tokens(tokens),
Self::ValidateUnsigned(inner) => inner.to_tokens(tokens),
Self::FreezeReason(inner) => inner.to_tokens(tokens),
Self::HoldReason(inner) => inner.to_tokens(tokens),
Self::Task(inner) => inner.to_tokens(tokens),
Self::LockId(inner) => inner.to_tokens(tokens),
Self::SlashReason(inner) => inner.to_tokens(tokens),
}
}
}
#[derive(Debug, Clone)]
pub struct PalletPart {
pub keyword: PalletPartKeyword,
pub generics: syn::Generics,
}
impl Parse for PalletPart {
fn parse(input: ParseStream) -> Result<Self> {
let keyword: PalletPartKeyword = input.parse()?;
let generics: syn::Generics = input.parse()?;
if !generics.params.is_empty() && !keyword.allows_generic() {
let valid_generics = PalletPart::format_names(PalletPartKeyword::all_generic_arg());
let msg = format!(
"`{}` is not allowed to have generics. \
Only the following pallets are allowed to have generics: {}.",
keyword.name(),
valid_generics,
);
return Err(syn::Error::new(keyword.span(), msg));
}
Ok(Self { keyword, generics })
}
}
impl PalletPart {
pub fn format_names(names: &[&'static str]) -> String {
let res: Vec<_> = names.iter().map(|s| format!("`{}`", s)).collect();
res.join(", ")
}
/// The name of this pallet part.
pub fn name(&self) -> &'static str {
self.keyword.name()
}
}
fn remove_kind(
input: ParseStream,
kind: WhereKind,
definitions: &mut Vec<WhereDefinition>,
) -> Result<WhereDefinition> {
if let Some(pos) = definitions.iter().position(|d| d.kind == kind) {
Ok(definitions.remove(pos))
} else {
let msg = format!(
"Missing associated type for `{:?}`. Add `{:?}` = ... to where section.",
kind, kind
);
Err(input.error(msg))
}
}
/// The declaration of a part without its generics
#[derive(Debug, Clone)]
pub struct PalletPartNoGeneric {
keyword: PalletPartKeyword,
}
impl Parse for PalletPartNoGeneric {
fn parse(input: ParseStream) -> Result<Self> {
Ok(Self { keyword: input.parse()? })
}
}
/// Parse [`PalletPartNoGeneric`]'s from a braces enclosed list that is split by commas, e.g.
///
/// `{ Call, Event }`
fn parse_pallet_parts_no_generic(input: ParseStream) -> Result<Vec<PalletPartNoGeneric>> {
let pezpallet_parts: ext::Braces<ext::Punctuated<PalletPartNoGeneric, Token![,]>> =
input.parse()?;
let mut resolved = HashSet::new();
for part in pezpallet_parts.content.inner.iter() {
if !resolved.insert(part.keyword.name()) {
let msg = format!(
"`{}` was already declared before. Please remove the duplicate declaration",
part.keyword.name(),
);
return Err(Error::new(part.keyword.span(), msg));
}
}
Ok(pezpallet_parts.content.inner.into_iter().collect())
}
/// The final definition of a pallet with the resulting fixed index and explicit parts.
#[derive(Debug, Clone)]
pub struct Pallet {
/// Is this pallet fully expanded?
pub is_expanded: bool,
/// The name of the pallet, e.g.`System` in `System: pezframe_system`.
pub name: Ident,
/// Either automatically inferred, or defined (e.g. `MyPallet ... = 3,`).
pub index: u8,
/// The path of the pallet, e.g. `pezframe_system` in `System: pezframe_system`.
pub path: PalletPath,
/// The instance of the pallet, e.g. `Instance1` in `Council: pezpallet_collective::<Instance1>`.
pub instance: Option<Ident>,
/// The pallet parts to use for the pallet.
pub pezpallet_parts: Vec<PalletPart>,
/// Expressions specified inside of a #[cfg] attribute.
pub cfg_pattern: Vec<cfg_expr::Expression>,
/// The doc literals
pub docs: Vec<syn::Expr>,
}
impl Pallet {
/// Get resolved pallet parts
pub fn pezpallet_parts(&self) -> &[PalletPart] {
&self.pezpallet_parts
}
/// Find matching parts
pub fn find_part(&self, name: &str) -> Option<&PalletPart> {
self.pezpallet_parts.iter().find(|part| part.name() == name)
}
/// Return whether pallet contains part
pub fn exists_part(&self, name: &str) -> bool {
self.find_part(name).is_some()
}
// Get runtime attributes for the pallet, mostly used for macros
pub fn get_attributes(&self) -> TokenStream {
self.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| {
let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original()))
.expect("was successfully parsed before; qed");
quote::quote! {
#acc
#attr
}
})
}
}
/// Result of a conversion of a declaration of pallets.
///
/// # State Transitions
///
/// ```ignore
/// +----------+ +----------+ +------------------+
/// | Implicit | -> | Explicit | -> | ExplicitExpanded |
/// +----------+ +----------+ +------------------+
/// ```
enum PalletsConversion {
/// Pallets implicitly declare parts.
///
/// `System: pezframe_system`.
Implicit(Vec<PalletDeclaration>),
/// Pallets explicitly declare parts.
///
/// `System: pezframe_system::{Pallet, Call}`
///
/// However, for backwards compatibility with Pezkuwi/Kusama
/// we must propagate some other parts to the pallet by default.
Explicit(Vec<Pallet>),
/// Pallets explicitly declare parts that are fully expanded.
///
/// This is the end state that contains extra parts included by
/// default by Bizinikiwi.
///
/// `System: pezframe_system expanded::{Error} ::{Pallet, Call}`
///
/// For this example, the `Pallet`, `Call` and `Error` parts are collected.
ExplicitExpanded(Vec<Pallet>),
}
/// Convert from the parsed pallet declaration to their final information.
///
/// Check if all pallet have explicit declaration of their parts, if so then assign index to each
/// pallet using same rules as rust for fieldless enum. I.e. implicit are assigned number
/// incrementally from last explicit or 0.
fn convert_pallets(pallets: Vec<PalletDeclaration>) -> syn::Result<PalletsConversion> {
if pallets.iter().any(|pallet| pallet.pezpallet_parts.is_none()) {
return Ok(PalletsConversion::Implicit(pallets));
}
let mut indices = HashMap::new();
let mut last_index: Option<u8> = None;
let mut names = HashMap::new();
let mut is_expanded = true;
let pallets = pallets
.into_iter()
.map(|pallet| {
let final_index = match pallet.index {
Some(i) => i,
None => last_index.map_or(Some(0), |i| i.checked_add(1)).ok_or_else(|| {
let msg = "Pallet index doesn't fit into u8, index is 256";
syn::Error::new(pallet.name.span(), msg)
})?,
};
last_index = Some(final_index);
if let Some(used_pallet) = indices.insert(final_index, pallet.name.clone()) {
let msg = format!(
"Pallet indices are conflicting: Both pallets {} and {} are at index {}",
used_pallet, pallet.name, final_index,
);
let mut err = syn::Error::new(used_pallet.span(), &msg);
err.combine(syn::Error::new(pallet.name.span(), msg));
return Err(err);
}
if let Some(used_pallet) = names.insert(pallet.name.clone(), pallet.name.span()) {
let msg = "Two pallets with the same name!";
let mut err = syn::Error::new(used_pallet, &msg);
err.combine(syn::Error::new(pallet.name.span(), &msg));
return Err(err);
}
let mut pezpallet_parts = pallet.pezpallet_parts.expect("Checked above");
let available_parts =
pezpallet_parts.iter().map(|part| part.keyword.name()).collect::<HashSet<_>>();
// Check parts are correctly specified
match &pallet.specified_parts {
SpecifiedParts::Exclude(parts) | SpecifiedParts::Use(parts) =>
for part in parts {
if !available_parts.contains(part.keyword.name()) {
let msg = format!(
"Invalid pallet part specified, the pallet `{}` doesn't have the \
`{}` part. Available parts are: {}.",
pallet.name,
part.keyword.name(),
pezpallet_parts.iter().fold(String::new(), |fold, part| {
if fold.is_empty() {
format!("`{}`", part.keyword.name())
} else {
format!("{}, `{}`", fold, part.keyword.name())
}
})
);
return Err(syn::Error::new(part.keyword.span(), msg));
}
},
SpecifiedParts::All => (),
}
// Set only specified parts.
match pallet.specified_parts {
SpecifiedParts::Exclude(excluded_parts) => pezpallet_parts.retain(|part| {
!excluded_parts
.iter()
.any(|excluded_part| excluded_part.keyword.name() == part.keyword.name())
}),
SpecifiedParts::Use(used_parts) => pezpallet_parts.retain(|part| {
used_parts.iter().any(|use_part| use_part.keyword.name() == part.keyword.name())
}),
SpecifiedParts::All => (),
}
let cfg_pattern = pallet
.attrs
.iter()
.map(|attr| {
if attr.path().segments.first().map_or(false, |s| s.ident != "cfg") {
let msg = "Unsupported attribute, only #[cfg] is supported on pallet \
declarations in `construct_runtime`";
return Err(syn::Error::new(attr.span(), msg));
}
attr.parse_args_with(|input: syn::parse::ParseStream| {
// Required, otherwise the parse stream doesn't advance and will result in
// an error.
let input = input.parse::<proc_macro2::TokenStream>()?;
cfg_expr::Expression::parse(&input.to_string())
.map_err(|e| syn::Error::new(attr.span(), e.to_string()))
})
})
.collect::<Result<Vec<_>>>()?;
is_expanded &= pallet.is_expanded;
Ok(Pallet {
is_expanded: pallet.is_expanded,
name: pallet.name,
index: final_index,
path: pallet.path,
instance: pallet.instance,
cfg_pattern,
pezpallet_parts,
docs: vec![],
})
})
.collect::<Result<Vec<_>>>()?;
if is_expanded {
Ok(PalletsConversion::ExplicitExpanded(pallets))
} else {
Ok(PalletsConversion::Explicit(pallets))
}
}
@@ -0,0 +1,54 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Implementation of macros related to crate versioning.
use super::get_cargo_env_var;
use pezframe_support_procedural_tools::generate_access_from_frame_or_crate;
use proc_macro2::{Span, TokenStream};
use syn::{Error, Result};
/// Create an error that will be shown by rustc at the call site of the macro.
fn create_error(message: &str) -> Error {
Error::new(Span::call_site(), message)
}
/// Implementation of the `crate_to_crate_version!` macro.
pub fn crate_to_crate_version(input: proc_macro::TokenStream) -> Result<TokenStream> {
if !input.is_empty() {
return Err(create_error("No arguments expected!"));
}
let major_version = get_cargo_env_var::<u16>("CARGO_PKG_VERSION_MAJOR")
.map_err(|_| create_error("Major version needs to fit into `u16`"))?;
let minor_version = get_cargo_env_var::<u8>("CARGO_PKG_VERSION_MINOR")
.map_err(|_| create_error("Minor version needs to fit into `u8`"))?;
let patch_version = get_cargo_env_var::<u8>("CARGO_PKG_VERSION_PATCH")
.map_err(|_| create_error("Patch version needs to fit into `u8`"))?;
let crate_ = generate_access_from_frame_or_crate("pezframe-support")?;
Ok(quote::quote! {
#crate_::traits::CrateVersion {
major: #major_version,
minor: #minor_version,
patch: #patch_version,
}
})
}
@@ -0,0 +1,235 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use proc_macro2::TokenStream;
use quote::quote;
use syn::{
punctuated::Punctuated, spanned::Spanned, Error, Expr, ExprLit, Lit, Meta, MetaNameValue,
Result, Token, Variant,
};
fn deprecation_msg_formatter(msg: &str) -> String {
format!(
r#"{msg}
help: the following are the possible correct uses
|
| #[deprecated = "reason"]
|
| #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
|
| #[deprecated]
|"#
)
}
// Should we generate a #crate_::__private::metadata_ir::ItemDeprecationInfoIR
// or a #crate_::__private::metadata_ir::VariantDeprecationInfoIR? In other words,
// are we targeting variant deprecation information, or generic item deprecation
// information.
#[derive(Copy, Clone, PartialEq)]
enum DeprecationTarget {
Item,
Variant,
}
fn parse_deprecated_meta(
crate_: &TokenStream,
attr: &syn::Attribute,
target: DeprecationTarget,
) -> Result<TokenStream> {
let target = match target {
DeprecationTarget::Item => {
quote! { #crate_::__private::metadata_ir::ItemDeprecationInfoIR }
},
DeprecationTarget::Variant => {
quote! { #crate_::__private::metadata_ir::VariantDeprecationInfoIR }
},
};
match &attr.meta {
Meta::List(meta_list) => {
let parsed = meta_list
.parse_args_with(Punctuated::<MetaNameValue, Token![,]>::parse_terminated)
.map_err(|e| Error::new(attr.span(), e.to_string()))?;
let (note, since) = parsed.iter().try_fold((None, None), |mut acc, item| {
let value = match &item.value {
Expr::Lit(ExprLit { lit: lit @ Lit::Str(_), .. }) => Ok(lit),
_ => Err(Error::new(
attr.span(),
deprecation_msg_formatter(
"Invalid deprecation attribute: expected string literal",
),
)),
}?;
if item.path.is_ident("note") {
acc.0.replace(value);
} else if item.path.is_ident("since") {
acc.1.replace(value);
}
Ok::<(Option<&syn::Lit>, Option<&syn::Lit>), Error>(acc)
})?;
note.map_or_else(
|| {
Err(Error::new(
attr.span(),
deprecation_msg_formatter("Invalid deprecation attribute: missing `note`"),
))
},
|note| {
let since = if let Some(str) = since {
quote! { Some(#str) }
} else {
quote! { None }
};
let doc = quote! { #target::Deprecated { note: #note, since: #since }};
Ok(doc)
},
)
},
Meta::NameValue(MetaNameValue {
value: Expr::Lit(ExprLit { lit: lit @ Lit::Str(_), .. }),
..
}) => {
// #[deprecated = "lit"]
let doc = quote! { #target::Deprecated { note: #lit, since: None } };
Ok(doc)
},
Meta::Path(_) => {
// #[deprecated]
Ok(quote! { #target::DeprecatedWithoutNote })
},
_ => Err(Error::new(
attr.span(),
deprecation_msg_formatter("Invalid deprecation attribute: expected string literal"),
)),
}
}
fn find_deprecation_attr(attrs: &[syn::Attribute]) -> Option<&syn::Attribute> {
attrs.iter().find(|a| a.path().is_ident("deprecated"))
}
fn parse_deprecation(
path: &TokenStream,
attrs: &[syn::Attribute],
target: DeprecationTarget,
) -> Result<Option<TokenStream>> {
find_deprecation_attr(attrs)
.map(|a| parse_deprecated_meta(path, a, target))
.transpose()
}
/// collects deprecation attribute if its present.
pub fn get_deprecation(path: &TokenStream, attrs: &[syn::Attribute]) -> Result<TokenStream> {
parse_deprecation(path, attrs, DeprecationTarget::Item).map(|item| {
item.unwrap_or_else(|| {
quote! {#path::__private::metadata_ir::ItemDeprecationInfoIR::NotDeprecated}
})
})
}
/// Call this on enum attributes to return an error if the #[deprecation] attribute is found.
pub fn prevent_deprecation_attr_on_outer_enum(parent_attrs: &[syn::Attribute]) -> Result<()> {
if let Some(attr) = find_deprecation_attr(parent_attrs) {
return Err(Error::new(
attr.span(),
"The `#[deprecated]` attribute should be applied to individual variants, not the enum as a whole.",
));
}
Ok(())
}
/// collects deprecation attribute if its present for enum-like types
pub fn get_deprecation_enum<'a>(
path: &TokenStream,
children_attrs: impl Iterator<Item = (u8, &'a [syn::Attribute])>,
) -> Result<TokenStream> {
let children = children_attrs
.filter_map(|(key, attributes)| {
let deprecation_status =
parse_deprecation(path, attributes, DeprecationTarget::Variant).transpose();
deprecation_status.map(|item| item.map(|item| quote::quote! { (#key, #item) }))
})
.collect::<Result<Vec<TokenStream>>>()?;
if children.is_empty() {
Ok(
quote::quote! { #path::__private::metadata_ir::EnumDeprecationInfoIR::nothing_deprecated() },
)
} else {
let children = quote::quote! { #path::__private::scale_info::prelude::collections::BTreeMap::from([#( #children),*]) };
Ok(quote::quote! { #path::__private::metadata_ir::EnumDeprecationInfoIR(#children) })
}
}
/// Gets the index for the variant inside `Error` or `Event` declaration.
/// priority is as follows:
/// Manual `#[codec(index = N)]`
/// Explicit discriminant `Variant = N`
/// Variant's definition index
pub fn variant_index_for_deprecation(index: u8, item: &Variant) -> u8 {
let index: u8 =
if let Some((_, Expr::Lit(ExprLit { lit: Lit::Int(num_lit), .. }))) = &item.discriminant {
num_lit.base10_parse::<u8>().unwrap_or(index as u8)
} else {
index as u8
};
item.attrs
.iter()
.find(|attr| attr.path().is_ident("codec"))
.and_then(|attr| {
if let Meta::List(meta_list) = &attr.meta {
meta_list
.parse_args_with(Punctuated::<MetaNameValue, syn::Token![,]>::parse_terminated)
.ok()
} else {
None
}
})
.and_then(|parsed| {
parsed.iter().fold(None, |mut acc, item| {
if let Expr::Lit(ExprLit { lit: Lit::Int(num_lit), .. }) = &item.value {
num_lit.base10_parse::<u8>().iter().for_each(|val| {
if item.path.is_ident("index") {
acc.replace(*val);
}
})
};
acc
})
})
.unwrap_or(index)
}
/// Filters all of the `allow` and `deprecated` attributes.
///
/// `allow` attributes are returned as is while `deprecated` attributes are replaced by
/// `#[allow(deprecated)]`.
pub fn extract_or_return_allow_attrs(
items: &[syn::Attribute],
) -> impl Iterator<Item = syn::Attribute> + '_ {
items.iter().filter_map(|attr| {
attr.path().is_ident("allow").then(|| attr.clone()).or_else(|| {
attr.path().is_ident("deprecated").then(|| {
syn::parse_quote! {
#[allow(deprecated)]
}
})
})
})
}
@@ -0,0 +1,363 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Implementation of the `derive_impl` attribute macro.
use macro_magic::mm_core::ForeignPath;
use proc_macro2::TokenStream as TokenStream2;
use quote::{quote, ToTokens};
use std::collections::HashSet;
use syn::{
parse2, parse_quote, spanned::Spanned, token, AngleBracketedGenericArguments, Ident, ImplItem,
ItemImpl, Path, PathArguments, PathSegment, Result, Token,
};
mod keyword {
syn::custom_keyword!(inject_runtime_type);
syn::custom_keyword!(no_aggregated_types);
}
#[derive(derive_syn_parse::Parse, PartialEq, Eq)]
pub enum PalletAttrType {
#[peek(keyword::inject_runtime_type, name = "inject_runtime_type")]
RuntimeType(keyword::inject_runtime_type),
}
#[derive(derive_syn_parse::Parse)]
pub struct PalletAttr {
_pound: Token![#],
#[bracket]
_bracket: token::Bracket,
#[inside(_bracket)]
typ: PalletAttrType,
}
fn is_runtime_type(item: &syn::ImplItemType) -> bool {
item.attrs.iter().any(|attr| {
if let Ok(PalletAttr { typ: PalletAttrType::RuntimeType(_), .. }) =
parse2::<PalletAttr>(attr.into_token_stream())
{
return true;
}
false
})
}
pub struct DeriveImplAttrArgs {
pub default_impl_path: Path,
pub generics: Option<AngleBracketedGenericArguments>,
_as: Option<Token![as]>,
pub disambiguation_path: Option<Path>,
_comma: Option<Token![,]>,
pub no_aggregated_types: Option<keyword::no_aggregated_types>,
}
impl syn::parse::Parse for DeriveImplAttrArgs {
fn parse(input: syn::parse::ParseStream) -> Result<Self> {
let mut default_impl_path: Path = input.parse()?;
// Extract the generics if any
let (default_impl_path, generics) = match default_impl_path.clone().segments.last() {
Some(PathSegment { ident, arguments: PathArguments::AngleBracketed(args) }) => {
default_impl_path.segments.pop();
default_impl_path
.segments
.push(PathSegment { ident: ident.clone(), arguments: PathArguments::None });
(default_impl_path, Some(args.clone()))
},
Some(PathSegment { arguments: PathArguments::None, .. }) => (default_impl_path, None),
_ => return Err(syn::Error::new(default_impl_path.span(), "Invalid default impl path")),
};
let lookahead = input.lookahead1();
let (_as, disambiguation_path) = if lookahead.peek(Token![as]) {
let _as: Token![as] = input.parse()?;
let disambiguation_path: Path = input.parse()?;
(Some(_as), Some(disambiguation_path))
} else {
(None, None)
};
let lookahead = input.lookahead1();
let (_comma, no_aggregated_types) = if lookahead.peek(Token![,]) {
let _comma: Token![,] = input.parse()?;
let no_aggregated_types: keyword::no_aggregated_types = input.parse()?;
(Some(_comma), Some(no_aggregated_types))
} else {
(None, None)
};
Ok(DeriveImplAttrArgs {
default_impl_path,
generics,
_as,
disambiguation_path,
_comma,
no_aggregated_types,
})
}
}
impl ForeignPath for DeriveImplAttrArgs {
fn foreign_path(&self) -> &Path {
&self.default_impl_path
}
}
impl ToTokens for DeriveImplAttrArgs {
fn to_tokens(&self, tokens: &mut TokenStream2) {
tokens.extend(self.default_impl_path.to_token_stream());
tokens.extend(self.generics.to_token_stream());
tokens.extend(self._as.to_token_stream());
tokens.extend(self.disambiguation_path.to_token_stream());
tokens.extend(self._comma.to_token_stream());
tokens.extend(self.no_aggregated_types.to_token_stream());
}
}
/// Gets the [`Ident`] representation of the given [`ImplItem`], if one exists. Otherwise
/// returns [`None`].
///
/// Used by [`combine_impls`] to determine whether we can compare [`ImplItem`]s by [`Ident`]
/// or not.
fn impl_item_ident(impl_item: &ImplItem) -> Option<&Ident> {
match impl_item {
ImplItem::Const(item) => Some(&item.ident),
ImplItem::Fn(item) => Some(&item.sig.ident),
ImplItem::Type(item) => Some(&item.ident),
ImplItem::Macro(item) => item.mac.path.get_ident(),
_ => None,
}
}
/// The real meat behind `derive_impl`. Takes in a `local_impl`, which is the impl for which we
/// want to implement defaults (i.e. the one the attribute macro is attached to), and a
/// `foreign_impl`, which is the impl containing the defaults we want to use, and returns an
/// [`ItemImpl`] containing the final generated impl.
///
/// This process has the following caveats:
/// * Colliding items that have an ident are not copied into `local_impl`
/// * Uncolliding items that have an ident are copied into `local_impl` but are qualified as `type
/// #ident = <#default_impl_path as #disambiguation_path>::#ident;`
/// * Items that lack an ident are de-duplicated so only unique items that lack an ident are copied
/// into `local_impl`. Items that lack an ident and also exist verbatim in `local_impl` are not
/// copied over.
fn combine_impls(
local_impl: ItemImpl,
foreign_impl: ItemImpl,
default_impl_path: Path,
disambiguation_path: Path,
inject_runtime_types: bool,
generics: Option<AngleBracketedGenericArguments>,
) -> ItemImpl {
let (existing_local_keys, existing_unsupported_items): (HashSet<ImplItem>, HashSet<ImplItem>) =
local_impl
.items
.iter()
.cloned()
.partition(|impl_item| impl_item_ident(impl_item).is_some());
let existing_local_keys: HashSet<Ident> = existing_local_keys
.into_iter()
.filter_map(|item| impl_item_ident(&item).cloned())
.collect();
let mut final_impl = local_impl;
let extended_items = foreign_impl.items.into_iter().filter_map(|item| {
if let Some(ident) = impl_item_ident(&item) {
if existing_local_keys.contains(&ident) {
// do not copy colliding items that have an ident
return None;
}
if let ImplItem::Type(typ) = item.clone() {
let cfg_attrs = typ
.attrs
.iter()
.filter(|attr| attr.path().get_ident().map_or(false, |ident| ident == "cfg"))
.map(|attr| attr.to_token_stream());
if is_runtime_type(&typ) {
let item: ImplItem = if inject_runtime_types {
parse_quote! {
#( #cfg_attrs )*
type #ident = #ident;
}
} else {
item
};
return Some(item);
}
// modify and insert uncolliding type items
let modified_item: ImplItem = parse_quote! {
#( #cfg_attrs )*
type #ident = <#default_impl_path #generics as #disambiguation_path>::#ident;
};
return Some(modified_item);
}
// copy uncolliding non-type items that have an ident
Some(item)
} else {
// do not copy colliding items that lack an ident
(!existing_unsupported_items.contains(&item))
// copy uncolliding items without an ident verbatim
.then_some(item)
}
});
final_impl.items.extend(extended_items);
final_impl
}
/// Computes the disambiguation path for the `derive_impl` attribute macro.
///
/// When specified explicitly using `as [disambiguation_path]` in the macro attr, the
/// disambiguation is used as is. If not, we infer the disambiguation path from the
/// `foreign_impl_path` and the computed scope.
fn compute_disambiguation_path(
disambiguation_path: Option<Path>,
foreign_impl: ItemImpl,
default_impl_path: Path,
) -> Result<Path> {
match (disambiguation_path, foreign_impl.clone().trait_) {
(Some(disambiguation_path), _) => Ok(disambiguation_path),
(None, Some((_, foreign_impl_path, _))) =>
if default_impl_path.segments.len() > 1 {
let scope = default_impl_path.segments.first();
Ok(parse_quote!(#scope :: #foreign_impl_path))
} else {
Ok(foreign_impl_path)
},
_ => Err(syn::Error::new(
default_impl_path.span(),
"Impl statement must have a defined type being implemented \
for a defined type such as `impl A for B`",
)),
}
}
/// Internal implementation behind [`#[derive_impl(..)]`](`macro@crate::derive_impl`).
///
/// `default_impl_path`: the module path of the external `impl` statement whose tokens we are
/// importing via `macro_magic`
///
/// `foreign_tokens`: the tokens for the external `impl` statement
///
/// `local_tokens`: the tokens for the local `impl` statement this attribute is attached to
///
/// `disambiguation_path`: the module path of the external trait we will use to qualify
/// defaults imported from the external `impl` statement
pub fn derive_impl(
default_impl_path: TokenStream2,
foreign_tokens: TokenStream2,
local_tokens: TokenStream2,
disambiguation_path: Option<Path>,
no_aggregated_types: Option<keyword::no_aggregated_types>,
generics: Option<AngleBracketedGenericArguments>,
) -> Result<TokenStream2> {
let local_impl = parse2::<ItemImpl>(local_tokens)?;
let foreign_impl = parse2::<ItemImpl>(foreign_tokens)?;
let default_impl_path = parse2::<Path>(default_impl_path)?;
let disambiguation_path = compute_disambiguation_path(
disambiguation_path,
foreign_impl.clone(),
default_impl_path.clone(),
)?;
// generate the combined impl
let combined_impl = combine_impls(
local_impl,
foreign_impl,
default_impl_path,
disambiguation_path,
no_aggregated_types.is_none(),
generics,
);
Ok(quote!(#combined_impl))
}
#[test]
fn test_derive_impl_attr_args_parsing() {
parse2::<DeriveImplAttrArgs>(quote!(
some::path::TestDefaultConfig as some::path::DefaultConfig
))
.unwrap();
parse2::<DeriveImplAttrArgs>(quote!(
pezframe_system::prelude::testing::TestDefaultConfig as DefaultConfig
))
.unwrap();
parse2::<DeriveImplAttrArgs>(quote!(Something as some::path::DefaultConfig)).unwrap();
parse2::<DeriveImplAttrArgs>(quote!(Something as DefaultConfig)).unwrap();
parse2::<DeriveImplAttrArgs>(quote!(DefaultConfig)).unwrap();
assert!(parse2::<DeriveImplAttrArgs>(quote!()).is_err());
assert!(parse2::<DeriveImplAttrArgs>(quote!(Config Config)).is_err());
}
#[test]
fn test_runtime_type_with_doc() {
#[allow(dead_code)]
trait TestTrait {
type Test;
}
#[allow(unused)]
struct TestStruct;
let p = parse2::<ItemImpl>(quote!(
impl TestTrait for TestStruct {
/// Some doc
#[inject_runtime_type]
type Test = u32;
}
))
.unwrap();
for item in p.items {
if let ImplItem::Type(typ) = item {
assert_eq!(is_runtime_type(&typ), true);
}
}
}
#[test]
fn test_disambiguation_path() {
let foreign_impl: ItemImpl = parse_quote!(impl SomeTrait for SomeType {});
let default_impl_path: Path = parse_quote!(SomeScope::SomeType);
// disambiguation path is specified
let disambiguation_path = compute_disambiguation_path(
Some(parse_quote!(SomeScope::SomePath)),
foreign_impl.clone(),
default_impl_path.clone(),
);
assert_eq!(disambiguation_path.unwrap(), parse_quote!(SomeScope::SomePath));
// disambiguation path is not specified and the default_impl_path has more than one segment
let disambiguation_path =
compute_disambiguation_path(None, foreign_impl.clone(), default_impl_path.clone());
assert_eq!(disambiguation_path.unwrap(), parse_quote!(SomeScope::SomeTrait));
// disambiguation path is not specified and the default_impl_path has only one segment
let disambiguation_path =
compute_disambiguation_path(None, foreign_impl.clone(), parse_quote!(SomeType));
assert_eq!(disambiguation_path.unwrap(), parse_quote!(SomeTrait));
}
#[test]
fn test_derive_impl_attr_args_parsing_with_generic() {
let args = parse2::<DeriveImplAttrArgs>(quote!(
some::path::TestDefaultConfig<Config> as some::path::DefaultConfig
))
.unwrap();
assert_eq!(args.default_impl_path, parse_quote!(some::path::TestDefaultConfig));
assert_eq!(args.generics.unwrap().args[0], parse_quote!(Config));
let args = parse2::<DeriveImplAttrArgs>(quote!(TestDefaultConfig<Config2>)).unwrap();
assert_eq!(args.default_impl_path, parse_quote!(TestDefaultConfig));
assert_eq!(args.generics.unwrap().args[0], parse_quote!(Config2));
}
@@ -0,0 +1,79 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::COUNTER;
use proc_macro::TokenStream;
pub fn generate_dummy_part_checker(input: TokenStream) -> TokenStream {
if !input.is_empty() {
return syn::Error::new(proc_macro2::Span::call_site(), "No arguments expected")
.to_compile_error()
.into();
}
let count = COUNTER.with(|counter| counter.borrow_mut().inc());
let no_op_macro_ident =
syn::Ident::new(&format!("__dummy_part_checker_{}", count), proc_macro2::Span::call_site());
quote::quote!(
#[macro_export]
#[doc(hidden)]
macro_rules! #no_op_macro_ident {
( $( $tt:tt )* ) => {};
}
#[doc(hidden)]
pub mod __bizinikiwi_genesis_config_check {
#[doc(hidden)]
pub use #no_op_macro_ident as is_genesis_config_defined;
#[doc(hidden)]
pub use #no_op_macro_ident as is_std_enabled_for_genesis;
}
#[doc(hidden)]
pub mod __bizinikiwi_event_check {
#[doc(hidden)]
pub use #no_op_macro_ident as is_event_part_defined;
}
#[doc(hidden)]
pub mod __bizinikiwi_inherent_check {
#[doc(hidden)]
pub use #no_op_macro_ident as is_inherent_part_defined;
}
#[doc(hidden)]
pub mod __bizinikiwi_validate_unsigned_check {
#[doc(hidden)]
pub use #no_op_macro_ident as is_validate_unsigned_part_defined;
}
#[doc(hidden)]
pub mod __bizinikiwi_call_check {
#[doc(hidden)]
pub use #no_op_macro_ident as is_call_part_defined;
}
#[doc(hidden)]
pub mod __bizinikiwi_origin_check {
#[doc(hidden)]
pub use #no_op_macro_ident as is_origin_part_defined;
}
)
.into()
}
@@ -0,0 +1,573 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Code for the `#[dynamic_params]`, `#[dynamic_pallet_params]` and
//! `#[dynamic_aggregated_params_internal]` macros.
use pezframe_support_procedural_tools::generate_access_from_frame_or_crate;
use inflector::Inflector;
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote, ToTokens};
use syn::{parse2, spanned::Spanned, visit_mut, visit_mut::VisitMut, Result, Token};
/// Parse and expand a `#[dynamic_params(..)]` module.
pub fn dynamic_params(attr: TokenStream, item: TokenStream) -> Result<TokenStream> {
DynamicParamModAttr::parse(attr, item).map(ToTokens::into_token_stream)
}
/// Parse and expand `#[dynamic_pallet_params(..)]` attribute.
pub fn dynamic_pallet_params(attr: TokenStream, item: TokenStream) -> Result<TokenStream> {
DynamicPalletParamAttr::parse(attr, item).map(ToTokens::into_token_stream)
}
/// Parse and expand `#[dynamic_aggregated_params_internal]` attribute.
pub fn dynamic_aggregated_params_internal(
_attr: TokenStream,
item: TokenStream,
) -> Result<TokenStream> {
parse2::<DynamicParamAggregatedEnum>(item).map(ToTokens::into_token_stream)
}
/// A top `#[dynamic_params(..)]` attribute together with a mod.
#[derive(derive_syn_parse::Parse)]
pub struct DynamicParamModAttr {
params_mod: syn::ItemMod,
meta: DynamicParamModAttrMeta,
}
/// The inner meta of a `#[dynamic_params(..)]` attribute.
#[derive(derive_syn_parse::Parse)]
pub struct DynamicParamModAttrMeta {
name: syn::Ident,
_comma: Option<Token![,]>,
#[parse_if(_comma.is_some())]
params_pallet: Option<syn::Type>,
}
impl DynamicParamModAttr {
pub fn parse(attr: TokenStream, item: TokenStream) -> Result<Self> {
let params_mod = parse2(item)?;
let meta = parse2(attr)?;
Ok(Self { params_mod, meta })
}
pub fn inner_mods(&self) -> Vec<syn::ItemMod> {
self.params_mod.content.as_ref().map_or(Vec::new(), |(_, items)| {
items
.iter()
.filter_map(|i| match i {
syn::Item::Mod(m) => Some(m),
_ => None,
})
.cloned()
.collect()
})
}
}
impl ToTokens for DynamicParamModAttr {
fn to_tokens(&self, tokens: &mut TokenStream) {
let scrate = match crate_access() {
Ok(path) => path,
Err(err) => return tokens.extend(err),
};
let (mut params_mod, name) = (self.params_mod.clone(), &self.meta.name);
let dynam_params_ident = &params_mod.ident;
let mut quoted_enum = quote! {};
for m in self.inner_mods() {
let aggregate_name =
syn::Ident::new(&m.ident.to_string().to_pascal_case(), m.ident.span());
let mod_name = &m.ident;
let mut attrs = m.attrs.clone();
attrs.retain(|attr| !attr.path().is_ident("dynamic_pallet_params"));
if let Err(err) = ensure_codec_index(&attrs, m.span()) {
tokens.extend(err.into_compile_error());
return;
}
quoted_enum.extend(quote! {
#(#attrs)*
#aggregate_name(#dynam_params_ident::#mod_name::Parameters),
});
}
// Inject the outer args into the inner `#[dynamic_pallet_params(..)]` attribute.
if let Some(params_pallet) = &self.meta.params_pallet {
MacroInjectArgs { runtime_params: name.clone(), params_pallet: params_pallet.clone() }
.visit_item_mod_mut(&mut params_mod);
}
tokens.extend(quote! {
#params_mod
#[#scrate::dynamic_params::dynamic_aggregated_params_internal]
pub enum #name {
#quoted_enum
}
});
}
}
/// Ensure there is a `#[codec(index = ..)]` attribute.
fn ensure_codec_index(attrs: &Vec<syn::Attribute>, span: Span) -> Result<()> {
let mut found = false;
for attr in attrs.iter() {
if attr.path().is_ident("codec") {
let meta: syn::ExprAssign = attr.parse_args()?;
if meta.left.to_token_stream().to_string() == "index" {
found = true;
break;
}
}
}
if !found {
Err(syn::Error::new(span, "Missing explicit `#[codec(index = ..)]` attribute"))
} else {
Ok(())
}
}
/// Used to inject arguments into the inner `#[dynamic_pallet_params(..)]` attribute.
///
/// This allows the outer `#[dynamic_params(..)]` attribute to specify some arguments that don't
/// need to be repeated every time.
struct MacroInjectArgs {
runtime_params: syn::Ident,
params_pallet: syn::Type,
}
impl VisitMut for MacroInjectArgs {
fn visit_item_mod_mut(&mut self, item: &mut syn::ItemMod) {
// Check if the mod has a `#[dynamic_pallet_params(..)]` attribute.
let attr = item.attrs.iter_mut().find(|attr| attr.path().is_ident("dynamic_pallet_params"));
if let Some(attr) = attr {
match &attr.meta {
syn::Meta::Path(path) => {
assert_eq!(path.to_token_stream().to_string(), "dynamic_pallet_params")
},
_ => (),
}
let runtime_params = &self.runtime_params;
let params_pallet = &self.params_pallet;
attr.meta = syn::parse2::<syn::Meta>(quote! {
dynamic_pallet_params(#runtime_params, #params_pallet)
})
.unwrap()
.into();
}
visit_mut::visit_item_mod_mut(self, item);
}
}
/// The helper attribute of a `#[dynamic_pallet_params(runtime_params, params_pallet)]`
/// attribute.
#[derive(derive_syn_parse::Parse)]
pub struct DynamicPalletParamAttr {
inner_mod: syn::ItemMod,
meta: DynamicPalletParamAttrMeta,
}
/// The inner meta of a `#[dynamic_pallet_params(..)]` attribute.
#[derive(derive_syn_parse::Parse)]
pub struct DynamicPalletParamAttrMeta {
runtime_params: syn::Ident,
_comma: Token![,],
parameter_pallet: syn::Type,
}
impl DynamicPalletParamAttr {
pub fn parse(attr: TokenStream, item: TokenStream) -> Result<Self> {
Ok(Self { inner_mod: parse2(item)?, meta: parse2(attr)? })
}
pub fn statics(&self) -> Vec<syn::ItemStatic> {
self.inner_mod.content.as_ref().map_or(Vec::new(), |(_, items)| {
items
.iter()
.filter_map(|i| match i {
syn::Item::Static(s) => Some(s),
_ => None,
})
.cloned()
.collect()
})
}
}
impl ToTokens for DynamicPalletParamAttr {
fn to_tokens(&self, tokens: &mut TokenStream) {
let scrate = match crate_access() {
Ok(path) => path,
Err(err) => return tokens.extend(err),
};
let (params_mod, parameter_pallet, runtime_params) =
(&self.inner_mod, &self.meta.parameter_pallet, &self.meta.runtime_params);
let aggregate_name = syn::Ident::new(
&params_mod.ident.to_string().to_pascal_case(),
params_mod.ident.span(),
);
let (mod_name, vis) = (&params_mod.ident, &params_mod.vis);
let statics = self.statics();
let (mut key_names, mut key_values, mut defaults, mut attrs, mut value_types): (
Vec<_>,
Vec<_>,
Vec<_>,
Vec<_>,
Vec<_>,
) = Default::default();
for s in statics.iter() {
if let Err(err) = ensure_codec_index(&s.attrs, s.span()) {
tokens.extend(err.into_compile_error());
return;
}
key_names.push(&s.ident);
key_values.push(format_ident!("{}Value", &s.ident));
defaults.push(&s.expr);
attrs.push(&s.attrs);
value_types.push(&s.ty);
}
let key_ident = syn::Ident::new("ParametersKey", params_mod.ident.span());
let value_ident = syn::Ident::new("ParametersValue", params_mod.ident.span());
let runtime_key_ident = format_ident!("{}Key", runtime_params);
let runtime_value_ident = format_ident!("{}Value", runtime_params);
tokens.extend(quote! {
pub mod #mod_name {
use super::*;
#[doc(hidden)]
#[derive(
Clone,
PartialEq,
Eq,
#scrate::__private::codec::Encode,
#scrate::__private::codec::Decode,
#scrate::__private::codec::DecodeWithMemTracking,
#scrate::__private::codec::MaxEncodedLen,
#scrate::__private::RuntimeDebug,
#scrate::__private::scale_info::TypeInfo
)]
#vis enum Parameters {
#(
#(#attrs)*
#key_names(#key_names, Option<#value_types>),
)*
}
#[doc(hidden)]
#[derive(
Clone,
PartialEq,
Eq,
#scrate::__private::codec::Encode,
#scrate::__private::codec::Decode,
#scrate::__private::codec::DecodeWithMemTracking,
#scrate::__private::codec::MaxEncodedLen,
#scrate::__private::RuntimeDebug,
#scrate::__private::scale_info::TypeInfo
)]
#vis enum #key_ident {
#(
#(#attrs)*
#key_names(#key_names),
)*
}
#[doc(hidden)]
#[derive(
Clone,
PartialEq,
Eq,
#scrate::__private::codec::Encode,
#scrate::__private::codec::Decode,
#scrate::__private::codec::DecodeWithMemTracking,
#scrate::__private::codec::MaxEncodedLen,
#scrate::__private::RuntimeDebug,
#scrate::__private::scale_info::TypeInfo
)]
#vis enum #value_ident {
#(
#(#attrs)*
#key_names(#value_types),
)*
}
impl #scrate::traits::dynamic_params::AggregatedKeyValue for Parameters {
type Key = #key_ident;
type Value = #value_ident;
fn into_parts(self) -> (Self::Key, Option<Self::Value>) {
match self {
#(
Parameters::#key_names(key, value) => {
(#key_ident::#key_names(key), value.map(#value_ident::#key_names))
},
)*
}
}
}
#(
#[doc(hidden)]
#[derive(
Clone,
PartialEq,
Eq,
#scrate::__private::codec::Encode,
#scrate::__private::codec::Decode,
#scrate::__private::codec::DecodeWithMemTracking,
#scrate::__private::codec::MaxEncodedLen,
#scrate::__private::RuntimeDebug,
#scrate::__private::scale_info::TypeInfo
)]
#vis struct #key_names;
impl #scrate::__private::Get<#value_types> for #key_names {
fn get() -> #value_types {
match
<#parameter_pallet as
#scrate::storage::StorageMap<#runtime_key_ident, #runtime_value_ident>
>::get(#runtime_key_ident::#aggregate_name(#key_ident::#key_names(#key_names)))
{
Some(#runtime_value_ident::#aggregate_name(
#value_ident::#key_names(inner))) => inner,
Some(_) => {
#scrate::defensive!("Unexpected value type at key - returning default");
#defaults
},
None => #defaults,
}
}
}
impl #scrate::traits::dynamic_params::Key for #key_names {
type Value = #value_types;
type WrappedValue = #key_values;
}
impl From<#key_names> for #key_ident {
fn from(key: #key_names) -> Self {
#key_ident::#key_names(key)
}
}
impl TryFrom<#key_ident> for #key_names {
type Error = ();
fn try_from(key: #key_ident) -> Result<Self, Self::Error> {
match key {
#key_ident::#key_names(key) => Ok(key),
_ => Err(()),
}
}
}
#[doc(hidden)]
#[derive(
Clone,
PartialEq,
Eq,
#scrate::pezsp_runtime::RuntimeDebug,
)]
#vis struct #key_values(pub #value_types);
impl From<#key_values> for #value_ident {
fn from(value: #key_values) -> Self {
#value_ident::#key_names(value.0)
}
}
impl From<(#key_names, #value_types)> for Parameters {
fn from((key, value): (#key_names, #value_types)) -> Self {
Parameters::#key_names(key, Some(value))
}
}
impl From<#key_names> for Parameters {
fn from(key: #key_names) -> Self {
Parameters::#key_names(key, None)
}
}
impl TryFrom<#value_ident> for #key_values {
type Error = ();
fn try_from(value: #value_ident) -> Result<Self, Self::Error> {
match value {
#value_ident::#key_names(value) => Ok(#key_values(value)),
_ => Err(()),
}
}
}
impl From<#key_values> for #value_types {
fn from(value: #key_values) -> Self {
value.0
}
}
)*
}
});
}
}
#[derive(derive_syn_parse::Parse)]
pub struct DynamicParamAggregatedEnum {
aggregated_enum: syn::ItemEnum,
}
impl ToTokens for DynamicParamAggregatedEnum {
fn to_tokens(&self, tokens: &mut TokenStream) {
let scrate = match crate_access() {
Ok(path) => path,
Err(err) => return tokens.extend(err),
};
let params_enum = &self.aggregated_enum;
let (name, vis) = (&params_enum.ident, &params_enum.vis);
let (mut indices, mut param_names, mut param_types): (Vec<_>, Vec<_>, Vec<_>) =
Default::default();
let mut attributes = Vec::new();
for (i, variant) in params_enum.variants.iter().enumerate() {
indices.push(i);
param_names.push(&variant.ident);
attributes.push(&variant.attrs);
param_types.push(match &variant.fields {
syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty,
_ => {
*tokens = quote! { compile_error!("Only unnamed enum variants with one inner item are supported") };
return
},
});
}
let params_key_ident = format_ident!("{}Key", params_enum.ident);
let params_value_ident = format_ident!("{}Value", params_enum.ident);
tokens.extend(quote! {
#[doc(hidden)]
#[derive(
Clone,
PartialEq,
Eq,
#scrate::__private::codec::Encode,
#scrate::__private::codec::Decode,
#scrate::__private::codec::DecodeWithMemTracking,
#scrate::__private::codec::MaxEncodedLen,
#scrate::pezsp_runtime::RuntimeDebug,
#scrate::__private::scale_info::TypeInfo
)]
#vis enum #name {
#(
//#[codec(index = #indices)]
#(#attributes)*
#param_names(#param_types),
)*
}
#[doc(hidden)]
#[derive(
Clone,
PartialEq,
Eq,
#scrate::__private::codec::Encode,
#scrate::__private::codec::Decode,
#scrate::__private::codec::DecodeWithMemTracking,
#scrate::__private::codec::MaxEncodedLen,
#scrate::pezsp_runtime::RuntimeDebug,
#scrate::__private::scale_info::TypeInfo
)]
#vis enum #params_key_ident {
#(
#(#attributes)*
#param_names(<#param_types as #scrate::traits::dynamic_params::AggregatedKeyValue>::Key),
)*
}
#[doc(hidden)]
#[derive(
Clone,
PartialEq,
Eq,
#scrate::__private::codec::Encode,
#scrate::__private::codec::Decode,
#scrate::__private::codec::DecodeWithMemTracking,
#scrate::__private::codec::MaxEncodedLen,
#scrate::pezsp_runtime::RuntimeDebug,
#scrate::__private::scale_info::TypeInfo
)]
#vis enum #params_value_ident {
#(
#(#attributes)*
#param_names(<#param_types as #scrate::traits::dynamic_params::AggregatedKeyValue>::Value),
)*
}
impl #scrate::traits::dynamic_params::AggregatedKeyValue for #name {
type Key = #params_key_ident;
type Value = #params_value_ident;
fn into_parts(self) -> (Self::Key, Option<Self::Value>) {
match self {
#(
#name::#param_names(parameter) => {
let (key, value) = parameter.into_parts();
(#params_key_ident::#param_names(key), value.map(#params_value_ident::#param_names))
},
)*
}
}
}
#(
impl ::core::convert::From<<#param_types as #scrate::traits::dynamic_params::AggregatedKeyValue>::Key> for #params_key_ident {
fn from(key: <#param_types as #scrate::traits::dynamic_params::AggregatedKeyValue>::Key) -> Self {
#params_key_ident::#param_names(key)
}
}
impl ::core::convert::TryFrom<#params_value_ident> for <#param_types as #scrate::traits::dynamic_params::AggregatedKeyValue>::Value {
type Error = ();
fn try_from(value: #params_value_ident) -> Result<Self, Self::Error> {
match value {
#params_value_ident::#param_names(value) => Ok(value),
_ => Err(()),
}
}
}
)*
});
}
}
/// Get access to the current crate and convert the error to a compile error.
fn crate_access() -> core::result::Result<syn::Path, TokenStream> {
generate_access_from_frame_or_crate("pezframe-support").map_err(|e| e.to_compile_error())
}
@@ -0,0 +1,104 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use pezframe_support_procedural_tools::generate_access_from_frame_or_crate;
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote, ToTokens};
use syn::{Ident, Result};
const MAX_IDENTS: usize = 18;
pub fn impl_key_prefix_for_tuples(input: proc_macro::TokenStream) -> Result<TokenStream> {
if !input.is_empty() {
return Err(syn::Error::new(Span::call_site(), "No arguments expected"));
}
let mut all_trait_impls = TokenStream::new();
let pezframe_support = generate_access_from_frame_or_crate("pezframe-support")?;
for i in 2..=MAX_IDENTS {
let current_tuple = (0..i)
.map(|n| Ident::new(&format!("Tuple{}", n), Span::call_site()))
.collect::<Vec<_>>();
for prefix_count in 1..i {
let (prefixes, suffixes) = current_tuple.split_at(prefix_count);
let hashers = current_tuple
.iter()
.map(|ident| format_ident!("Hasher{}", ident))
.collect::<Vec<_>>();
let kargs =
prefixes.iter().map(|ident| format_ident!("KArg{}", ident)).collect::<Vec<_>>();
let partial_keygen = generate_keygen(prefixes);
let suffix_keygen = generate_keygen(suffixes);
let suffix_tuple = generate_tuple(suffixes);
let trait_impls = quote! {
impl<
#(#current_tuple: FullCodec + StaticTypeInfo,)*
#(#hashers: StorageHasher,)*
#(#kargs: EncodeLike<#prefixes>),*
> HasKeyPrefix<( #( #kargs, )* )> for ( #( Key<#hashers, #current_tuple>, )* ) {
type Suffix = #suffix_tuple;
fn partial_key(prefix: ( #( #kargs, )* )) -> Vec<u8> {
<#partial_keygen>::final_key(prefix)
}
}
impl<
#(#current_tuple: FullCodec + StaticTypeInfo,)*
#(#hashers: ReversibleStorageHasher,)*
#(#kargs: EncodeLike<#prefixes>),*
> HasReversibleKeyPrefix<( #( #kargs, )* )> for ( #( Key<#hashers, #current_tuple>, )* ) {
fn decode_partial_key(key_material: &[u8]) -> Result<
Self::Suffix,
#pezframe_support::__private::codec::Error,
> {
<#suffix_keygen>::decode_final_key(key_material).map(|k| k.0)
}
}
};
all_trait_impls.extend(trait_impls);
}
}
Ok(all_trait_impls)
}
fn generate_tuple(idents: &[Ident]) -> TokenStream {
if idents.len() == 1 {
idents[0].to_token_stream()
} else {
quote!((#(#idents),*))
}
}
fn generate_keygen(idents: &[Ident]) -> TokenStream {
if idents.len() == 1 {
let key = &idents[0];
let hasher = format_ident!("Hasher{}", key);
quote!(Key<#hasher, #key>)
} else {
let hashers = idents.iter().map(|ident| format_ident!("Hasher{}", ident));
quote!((#(Key<#hashers, #idents>),*))
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,159 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Implementation of the `match_and_insert` macro.
use proc_macro2::{Group, Span, TokenStream, TokenTree};
use std::iter::once;
use syn::spanned::Spanned;
mod keyword {
syn::custom_keyword!(target);
syn::custom_keyword!(pattern);
syn::custom_keyword!(tokens);
}
pub fn match_and_insert(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let MatchAndInsertDef { pattern, tokens, target } =
syn::parse_macro_input!(input as MatchAndInsertDef);
match expand_in_stream(&pattern, &mut Some(tokens), target) {
Ok(stream) => stream.into(),
Err(err) => err.to_compile_error().into(),
}
}
struct MatchAndInsertDef {
// Token stream to search and insert tokens into.
target: TokenStream,
// Pattern to match against, this is ensured to have no TokenTree::Group nor TokenTree::Literal
// (i.e. contains only Punct or Ident), and not being empty.
pattern: Vec<TokenTree>,
// Token stream to insert after the match pattern.
tokens: TokenStream,
}
impl syn::parse::Parse for MatchAndInsertDef {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut target;
input.parse::<keyword::target>()?;
input.parse::<syn::Token![=]>()?;
let _replace_with_bracket: syn::token::Bracket = syn::bracketed!(target in input);
let _replace_with_brace: syn::token::Brace = syn::braced!(target in target);
let target = target.parse()?;
let mut pattern;
input.parse::<keyword::pattern>()?;
input.parse::<syn::Token![=]>()?;
let _replace_with_bracket: syn::token::Bracket = syn::bracketed!(pattern in input);
let _replace_with_brace: syn::token::Brace = syn::braced!(pattern in pattern);
let pattern = pattern.parse::<TokenStream>()?.into_iter().collect::<Vec<TokenTree>>();
if let Some(t) = pattern.iter().find(|t| matches!(t, TokenTree::Group(_))) {
return Err(syn::Error::new(t.span(), "Unexpected group token tree"));
}
if let Some(t) = pattern.iter().find(|t| matches!(t, TokenTree::Literal(_))) {
return Err(syn::Error::new(t.span(), "Unexpected literal token tree"));
}
if pattern.is_empty() {
return Err(syn::Error::new(Span::call_site(), "empty match pattern is invalid"));
}
let mut tokens;
input.parse::<keyword::tokens>()?;
input.parse::<syn::Token![=]>()?;
let _replace_with_bracket: syn::token::Bracket = syn::bracketed!(tokens in input);
let _replace_with_brace: syn::token::Brace = syn::braced!(tokens in tokens);
let tokens = tokens.parse()?;
Ok(Self { tokens, pattern, target })
}
}
// Insert `tokens` after the first matching `pattern`.
// `tokens` must be some (Option is used for internal simplification).
// `pattern` must not be empty and should only contain Ident or Punct.
fn expand_in_stream(
pattern: &[TokenTree],
tokens: &mut Option<TokenStream>,
stream: TokenStream,
) -> syn::Result<TokenStream> {
assert!(
tokens.is_some(),
"`tokens` must be some, Option is used because `tokens` is used only once"
);
assert!(
!pattern.is_empty(),
"`pattern` must not be empty, otherwise there is nothing to match against"
);
let stream_span = stream.span();
let mut stream = stream.into_iter();
let mut extended = TokenStream::new();
let mut match_cursor = 0;
while let Some(token) = stream.next() {
match token {
TokenTree::Group(group) => {
match_cursor = 0;
let group_stream = group.stream();
match expand_in_stream(pattern, tokens, group_stream) {
Ok(s) => {
extended.extend(once(TokenTree::Group(Group::new(group.delimiter(), s))));
extended.extend(stream);
return Ok(extended);
},
Err(_) => {
extended.extend(once(TokenTree::Group(group)));
},
}
},
other => {
advance_match_cursor(&other, pattern, &mut match_cursor);
extended.extend(once(other));
if match_cursor == pattern.len() {
extended
.extend(once(tokens.take().expect("tokens is used to replace only once")));
extended.extend(stream);
return Ok(extended);
}
},
}
}
// if we reach this point, it means the stream is empty and we haven't found a matching pattern
let msg = format!("Cannot find pattern `{:?}` in given token stream", pattern);
Err(syn::Error::new(stream_span, msg))
}
fn advance_match_cursor(other: &TokenTree, pattern: &[TokenTree], match_cursor: &mut usize) {
use TokenTree::{Ident, Punct};
let does_match_other_pattern = match (other, &pattern[*match_cursor]) {
(Ident(i1), Ident(i2)) => i1 == i2,
(Punct(p1), Punct(p2)) => p1.as_char() == p2.as_char(),
_ => false,
};
if does_match_other_pattern {
*match_cursor += 1;
} else {
*match_cursor = 0;
}
}
@@ -0,0 +1,108 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use syn::spanned::Spanned;
/// Derive Clone but do not bound any generic.
pub fn derive_clone_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = syn::parse_macro_input!(input as syn::DeriveInput);
let name = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let impl_ = match input.data {
syn::Data::Struct(struct_) => match struct_.fields {
syn::Fields::Named(named) => {
let fields = named.named.iter().map(|i| &i.ident).map(|i| {
quote::quote_spanned!(i.span() =>
#i: ::core::clone::Clone::clone(&self.#i)
)
});
quote::quote!( Self { #( #fields, )* } )
},
syn::Fields::Unnamed(unnamed) => {
let fields =
unnamed.unnamed.iter().enumerate().map(|(i, _)| syn::Index::from(i)).map(|i| {
quote::quote_spanned!(i.span() =>
::core::clone::Clone::clone(&self.#i)
)
});
quote::quote!( Self ( #( #fields, )* ) )
},
syn::Fields::Unit => {
quote::quote!(Self)
},
},
syn::Data::Enum(enum_) => {
let variants = enum_.variants.iter().map(|variant| {
let ident = &variant.ident;
match &variant.fields {
syn::Fields::Named(named) => {
let captured = named.named.iter().map(|i| &i.ident);
let cloned = captured.clone().map(|i| {
::quote::quote_spanned!(i.span() =>
#i: ::core::clone::Clone::clone(#i)
)
});
quote::quote!(
Self::#ident { #( ref #captured, )* } => Self::#ident { #( #cloned, )*}
)
},
syn::Fields::Unnamed(unnamed) => {
let captured = unnamed
.unnamed
.iter()
.enumerate()
.map(|(i, f)| syn::Ident::new(&format!("_{}", i), f.span()));
let cloned = captured.clone().map(|i| {
quote::quote_spanned!(i.span() =>
::core::clone::Clone::clone(#i)
)
});
quote::quote!(
Self::#ident ( #( ref #captured, )* ) => Self::#ident ( #( #cloned, )*)
)
},
syn::Fields::Unit => quote::quote!( Self::#ident => Self::#ident ),
}
});
quote::quote!(match self {
#( #variants, )*
})
},
syn::Data::Union(_) => {
let msg = "Union type not supported by `derive(CloneNoBound)`";
return syn::Error::new(input.span(), msg).to_compile_error().into();
},
};
quote::quote!(
const _: () = {
#[automatically_derived]
#[allow(deprecated)]
impl #impl_generics ::core::clone::Clone for #name #ty_generics #where_clause {
fn clone(&self) -> Self {
#impl_
}
}
};
)
.into()
}
@@ -0,0 +1,122 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use syn::spanned::Spanned;
/// Derive Debug but do not bound any generics.
pub fn derive_debug_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = syn::parse_macro_input!(input as syn::DeriveInput);
let input_ident = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let impl_ = match input.data {
syn::Data::Struct(struct_) => match struct_.fields {
syn::Fields::Named(named) => {
let fields =
named.named.iter().map(|i| &i.ident).map(
|i| quote::quote_spanned!(i.span() => .field(stringify!(#i), &self.#i) ),
);
quote::quote!(
fmt.debug_struct(stringify!(#input_ident))
#( #fields )*
.finish()
)
},
syn::Fields::Unnamed(unnamed) => {
let fields = unnamed
.unnamed
.iter()
.enumerate()
.map(|(i, _)| syn::Index::from(i))
.map(|i| quote::quote_spanned!(i.span() => .field(&self.#i) ));
quote::quote!(
fmt.debug_tuple(stringify!(#input_ident))
#( #fields )*
.finish()
)
},
syn::Fields::Unit => quote::quote!(fmt.write_str(stringify!(#input_ident))),
},
syn::Data::Enum(enum_) => {
let variants = enum_.variants.iter().map(|variant| {
let ident = &variant.ident;
let full_variant_str = format!("{}::{}", input_ident, ident);
match &variant.fields {
syn::Fields::Named(named) => {
let captured = named.named.iter().map(|i| &i.ident);
let debugged = captured.clone().map(|i| {
quote::quote_spanned!(i.span() =>
.field(stringify!(#i), &#i)
)
});
quote::quote!(
Self::#ident { #( ref #captured, )* } => {
fmt.debug_struct(#full_variant_str)
#( #debugged )*
.finish()
}
)
},
syn::Fields::Unnamed(unnamed) => {
let captured = unnamed
.unnamed
.iter()
.enumerate()
.map(|(i, f)| syn::Ident::new(&format!("_{}", i), f.span()));
let debugged = captured
.clone()
.map(|i| quote::quote_spanned!(i.span() => .field(&#i)));
quote::quote!(
Self::#ident ( #( ref #captured, )* ) => {
fmt.debug_tuple(#full_variant_str)
#( #debugged )*
.finish()
}
)
},
syn::Fields::Unit => quote::quote!(
Self::#ident => fmt.write_str(#full_variant_str)
),
}
});
quote::quote!(match *self {
#( #variants, )*
})
},
syn::Data::Union(_) => {
let msg = "Union type not supported by `derive(DebugNoBound)`";
return syn::Error::new(input.span(), msg).to_compile_error().into();
},
};
quote::quote!(
const _: () = {
#[automatically_derived]
#[allow(deprecated)]
impl #impl_generics ::core::fmt::Debug for #input_ident #ty_generics #where_clause {
fn fmt(&self, fmt: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
#impl_
}
}
};
)
.into()
}
@@ -0,0 +1,162 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use proc_macro2::Span;
use quote::{quote, quote_spanned};
use syn::{spanned::Spanned, Data, DeriveInput, Fields};
/// Derive Default but do not bound any generic.
pub fn derive_default_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = syn::parse_macro_input!(input as DeriveInput);
let name = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let impl_ = match input.data {
Data::Struct(struct_) => match struct_.fields {
Fields::Named(named) => {
let fields = named.named.iter().map(|field| &field.ident).map(|ident| {
quote_spanned! {ident.span() =>
#ident: ::core::default::Default::default()
}
});
quote!(Self { #( #fields, )* })
},
Fields::Unnamed(unnamed) => {
let fields = unnamed.unnamed.iter().map(|field| {
quote_spanned! {field.span()=>
::core::default::Default::default()
}
});
quote!(Self( #( #fields, )* ))
},
Fields::Unit => {
quote!(Self)
},
},
Data::Enum(enum_) => {
if enum_.variants.is_empty() {
return syn::Error::new_spanned(name, "cannot derive Default for an empty enum")
.to_compile_error()
.into();
}
// all #[default] attrs with the variant they're on; i.e. a var
let default_variants = enum_
.variants
.into_iter()
.filter(|variant| variant.attrs.iter().any(|attr| attr.path().is_ident("default")))
.collect::<Vec<_>>();
match &*default_variants {
[] => return syn::Error::new(
name.clone().span(),
"no default declared, make a variant default by placing `#[default]` above it",
)
.into_compile_error()
.into(),
// only one variant with the #[default] attribute set
[default_variant] => {
let variant_attrs = default_variant
.attrs
.iter()
.filter(|a| a.path().is_ident("default"))
.collect::<Vec<_>>();
// check that there is only one #[default] attribute on the variant
if let [first_attr, second_attr, additional_attrs @ ..] = &*variant_attrs {
let mut err =
syn::Error::new(Span::call_site(), "multiple `#[default]` attributes");
err.combine(syn::Error::new_spanned(first_attr, "`#[default]` used here"));
err.extend([second_attr].into_iter().chain(additional_attrs).map(
|variant| {
syn::Error::new_spanned(variant, "`#[default]` used again here")
},
));
return err.into_compile_error().into();
}
let variant_ident = &default_variant.ident;
let fully_qualified_variant_path = quote!(Self::#variant_ident);
match &default_variant.fields {
Fields::Named(named) => {
let fields =
named.named.iter().map(|field| &field.ident).map(|ident| {
quote_spanned! {ident.span()=>
#ident: ::core::default::Default::default()
}
});
quote!(#fully_qualified_variant_path { #( #fields, )* })
},
Fields::Unnamed(unnamed) => {
let fields = unnamed.unnamed.iter().map(|field| {
quote_spanned! {field.span()=>
::core::default::Default::default()
}
});
quote!(#fully_qualified_variant_path( #( #fields, )* ))
},
Fields::Unit => fully_qualified_variant_path,
}
},
[first, additional @ ..] => {
let mut err = syn::Error::new(Span::call_site(), "multiple declared defaults");
err.combine(syn::Error::new_spanned(first, "first default"));
err.extend(
additional
.into_iter()
.map(|variant| syn::Error::new_spanned(variant, "additional default")),
);
return err.into_compile_error().into();
},
}
},
Data::Union(union_) =>
return syn::Error::new_spanned(
union_.union_token,
"Union type not supported by `derive(DefaultNoBound)`",
)
.to_compile_error()
.into(),
};
quote!(
const _: () = {
#[automatically_derived]
#[allow(deprecated)]
impl #impl_generics ::core::default::Default for #name #ty_generics #where_clause {
fn default() -> Self {
#impl_
}
}
};
)
.into()
}
@@ -0,0 +1,25 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Derive macros to derive traits without bounding generic parameters.
pub mod clone;
pub mod debug;
pub mod default;
pub mod ord;
pub mod partial_eq;
pub mod partial_ord;
@@ -0,0 +1,76 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use syn::spanned::Spanned;
/// Derive Ord but do not bound any generic.
pub fn derive_ord_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input: syn::DeriveInput = match syn::parse(input) {
Ok(input) => input,
Err(e) => return e.to_compile_error().into(),
};
let name = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let impl_ = match input.data {
syn::Data::Struct(struct_) => match struct_.fields {
syn::Fields::Named(named) => {
let fields = named
.named
.iter()
.map(|i| &i.ident)
.map(|i| quote::quote_spanned!(i.span() => self.#i.cmp(&other.#i) ));
quote::quote!( core::cmp::Ordering::Equal #( .then_with(|| #fields) )* )
},
syn::Fields::Unnamed(unnamed) => {
let fields = unnamed
.unnamed
.iter()
.enumerate()
.map(|(i, _)| syn::Index::from(i))
.map(|i| quote::quote_spanned!(i.span() => self.#i.cmp(&other.#i) ));
quote::quote!( core::cmp::Ordering::Equal #( .then_with(|| #fields) )* )
},
syn::Fields::Unit => {
quote::quote!(core::cmp::Ordering::Equal)
},
},
syn::Data::Enum(_) => {
let msg = "Enum type not supported by `derive(OrdNoBound)`";
return syn::Error::new(input.span(), msg).to_compile_error().into();
},
syn::Data::Union(_) => {
let msg = "Union type not supported by `derive(OrdNoBound)`";
return syn::Error::new(input.span(), msg).to_compile_error().into();
},
};
quote::quote!(
const _: () = {
#[allow(deprecated)]
impl #impl_generics core::cmp::Ord for #name #ty_generics #where_clause {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
#impl_
}
}
};
)
.into()
}
@@ -0,0 +1,138 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use syn::spanned::Spanned;
/// Derive PartialEq but do not bound any generic.
pub fn derive_partial_eq_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = syn::parse_macro_input!(input as syn::DeriveInput);
let name = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let impl_ = match input.data {
syn::Data::Struct(struct_) => match struct_.fields {
syn::Fields::Named(named) => {
let fields = named
.named
.iter()
.map(|i| &i.ident)
.map(|i| quote::quote_spanned!(i.span() => self.#i == other.#i ));
quote::quote!( true #( && #fields )* )
},
syn::Fields::Unnamed(unnamed) => {
let fields = unnamed
.unnamed
.iter()
.enumerate()
.map(|(i, _)| syn::Index::from(i))
.map(|i| quote::quote_spanned!(i.span() => self.#i == other.#i ));
quote::quote!( true #( && #fields )* )
},
syn::Fields::Unit => {
quote::quote!(true)
},
},
syn::Data::Enum(enum_) => {
let variants =
enum_.variants.iter().map(|variant| {
let ident = &variant.ident;
match &variant.fields {
syn::Fields::Named(named) => {
let names = named.named.iter().map(|i| &i.ident);
let other_names = names.clone().enumerate().map(|(n, ident)| {
syn::Ident::new(&format!("_{}", n), ident.span())
});
let capture = names.clone();
let other_capture = names
.clone()
.zip(other_names.clone())
.map(|(i, other_i)| quote::quote!(#i: #other_i));
let eq = names.zip(other_names).map(
|(i, other_i)| quote::quote_spanned!(i.span() => #i == #other_i),
);
quote::quote!(
(
Self::#ident { #( #capture, )* },
Self::#ident { #( #other_capture, )* },
) => true #( && #eq )*
)
},
syn::Fields::Unnamed(unnamed) => {
let names = unnamed
.unnamed
.iter()
.enumerate()
.map(|(i, f)| syn::Ident::new(&format!("_{}", i), f.span()));
let other_names =
unnamed.unnamed.iter().enumerate().map(|(i, f)| {
syn::Ident::new(&format!("_{}_other", i), f.span())
});
let eq = names.clone().zip(other_names.clone()).map(
|(i, other_i)| quote::quote_spanned!(i.span() => #i == #other_i),
);
quote::quote!(
(
Self::#ident ( #( #names, )* ),
Self::#ident ( #( #other_names, )* ),
) => true #( && #eq )*
)
},
syn::Fields::Unit => quote::quote!( (Self::#ident, Self::#ident) => true ),
}
});
let mut different_variants = vec![];
for (i, i_variant) in enum_.variants.iter().enumerate() {
for (j, j_variant) in enum_.variants.iter().enumerate() {
if i != j {
let i_ident = &i_variant.ident;
let j_ident = &j_variant.ident;
different_variants.push(quote::quote!(
(Self::#i_ident { .. }, Self::#j_ident { .. }) => false
))
}
}
}
quote::quote!( match (self, other) {
#( #variants, )*
#( #different_variants, )*
})
},
syn::Data::Union(_) => {
let msg = "Union type not supported by `derive(PartialEqNoBound)`";
return syn::Error::new(input.span(), msg).to_compile_error().into();
},
};
quote::quote!(
const _: () = {
#[automatically_derived]
#[allow(deprecated)]
impl #impl_generics ::core::cmp::PartialEq for #name #ty_generics #where_clause {
fn eq(&self, other: &Self) -> bool {
#impl_
}
}
};
)
.into()
}
@@ -0,0 +1,90 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use syn::spanned::Spanned;
/// Derive PartialOrd but do not bound any generic.
pub fn derive_partial_ord_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input: syn::DeriveInput = match syn::parse(input) {
Ok(input) => input,
Err(e) => return e.to_compile_error().into(),
};
let name = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let impl_ = match input.data {
syn::Data::Struct(struct_) =>
match struct_.fields {
syn::Fields::Named(named) => {
let fields =
named.named.iter().map(|i| &i.ident).map(
|i| quote::quote_spanned!(i.span() => self.#i.partial_cmp(&other.#i)),
);
quote::quote!(
Some(core::cmp::Ordering::Equal)
#(
.and_then(|order| {
let next_order = #fields?;
Some(order.then(next_order))
})
)*
)
},
syn::Fields::Unnamed(unnamed) => {
let fields =
unnamed.unnamed.iter().enumerate().map(|(i, _)| syn::Index::from(i)).map(
|i| quote::quote_spanned!(i.span() => self.#i.partial_cmp(&other.#i)),
);
quote::quote!(
Some(core::cmp::Ordering::Equal)
#(
.and_then(|order| {
let next_order = #fields?;
Some(order.then(next_order))
})
)*
)
},
syn::Fields::Unit => {
quote::quote!(Some(core::cmp::Ordering::Equal))
},
},
syn::Data::Enum(_) => {
let msg = "Enum type not supported by `derive(PartialOrdNoBound)`";
return syn::Error::new(input.span(), msg).to_compile_error().into();
},
syn::Data::Union(_) => {
let msg = "Union type not supported by `derive(PartialOrdNoBound)`";
return syn::Error::new(input.span(), msg).to_compile_error().into();
},
};
quote::quote!(
const _: () = {
#[allow(deprecated)]
impl #impl_generics core::cmp::PartialOrd for #name #ty_generics #where_clause {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
#impl_
}
}
};
)
.into()
}
@@ -0,0 +1,614 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
deprecation::extract_or_return_allow_attrs,
pallet::{
expand::warnings::{weight_constant_warning, weight_witness_warning},
parse::{
call::{CallVariantDef, CallWeightDef},
helper::CallReturnType,
},
Def,
},
COUNTER,
};
use proc_macro2::TokenStream as TokenStream2;
use proc_macro_warning::Warning;
use quote::{quote, ToTokens};
use syn::spanned::Spanned;
/// Expand the weight to final token stream and accumulate warnings.
fn expand_weight(
prefix: &str,
pezframe_support: &syn::Path,
dev_mode: bool,
weight_warnings: &mut Vec<Warning>,
method: &CallVariantDef,
weight: &CallWeightDef,
) -> TokenStream2 {
match weight {
CallWeightDef::DevModeDefault => quote::quote!(
#pezframe_support::pezpallet_prelude::Weight::zero()
),
CallWeightDef::Immediate(e) => {
weight_constant_warning(e, dev_mode, weight_warnings);
weight_witness_warning(method, dev_mode, weight_warnings);
e.into_token_stream()
},
CallWeightDef::Inherited(t) => {
// Expand `<<T as Config>::WeightInfo>::$prefix$call_name()`.
let n = &syn::Ident::new(&format!("{}{}", prefix, method.name), method.name.span());
quote!({ < #t > :: #n () })
},
}
}
///
/// * Generate enum call and implement various trait on it.
/// * Implement Callable and call_function on `Pallet`
pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream {
let (span, where_clause, methods, docs) = match def.call.as_ref() {
Some(call) => {
let span = call.attr_span;
let where_clause = call.where_clause.clone();
let methods = call.methods.clone();
let docs = call.docs.clone();
(span, where_clause, methods, docs)
},
None => (def.item.span(), def.config.where_clause.clone(), Vec::new(), Vec::new()),
};
let pezframe_support = &def.pezframe_support;
let pezframe_system = &def.pezframe_system;
let type_impl_gen = &def.type_impl_generics(span);
let type_decl_bounded_gen = &def.type_decl_bounded_generics(span);
let type_use_gen = &def.type_use_generics(span);
let call_ident = syn::Ident::new("Call", span);
let pezpallet_ident = &def.pezpallet_struct.pallet;
let fn_name = methods.iter().map(|method| &method.name).collect::<Vec<_>>();
let call_index = methods.iter().map(|method| method.call_index).collect::<Vec<_>>();
let new_call_variant_fn_name = fn_name
.iter()
.map(|fn_name| quote::format_ident!("new_call_variant_{}", fn_name))
.collect::<Vec<_>>();
let new_call_variant_doc = fn_name
.iter()
.map(|fn_name| format!("Create a call with the variant `{}`.", fn_name))
.collect::<Vec<_>>();
let mut call_index_warnings = Vec::new();
// Emit a warning for each call that is missing `call_index` when not in dev-mode.
for method in &methods {
if method.explicit_call_index || def.dev_mode {
continue;
}
let warning = Warning::new_deprecated("ImplicitCallIndex")
.index(call_index_warnings.len())
.old("use implicit call indices")
.new("ensure that all calls have a `pallet::call_index` attribute or put the pallet into `dev` mode")
.help_links(&[
"https://github.com/pezkuwichain/kurdistan-sdk/issues/39",
"https://github.com/pezkuwichain/kurdistan-sdk/issues/36"
])
.span(method.name.span())
.build_or_panic();
call_index_warnings.push(warning);
}
let mut fn_weight = Vec::<TokenStream2>::new();
let mut weight_warnings = Vec::new();
for method in &methods {
let w = expand_weight(
"",
pezframe_support,
def.dev_mode,
&mut weight_warnings,
method,
&method.weight,
);
fn_weight.push(w);
}
debug_assert_eq!(fn_weight.len(), methods.len());
let fn_doc = methods.iter().map(|method| &method.docs).collect::<Vec<_>>();
let args_name = methods
.iter()
.map(|method| method.args.iter().map(|(_, name, _)| name.clone()).collect::<Vec<_>>())
.collect::<Vec<_>>();
let args_name_stripped = methods
.iter()
.map(|method| {
method
.args
.iter()
.map(|(_, name, _)| {
syn::Ident::new(name.to_string().trim_start_matches('_'), name.span())
})
.collect::<Vec<_>>()
})
.collect::<Vec<_>>();
let make_args_name_pattern = |ref_tok| {
args_name
.iter()
.zip(args_name_stripped.iter())
.map(|(args_name, args_name_stripped)| {
args_name
.iter()
.zip(args_name_stripped)
.map(|(args_name, args_name_stripped)| {
if args_name == args_name_stripped {
quote::quote!( #ref_tok #args_name )
} else {
quote::quote!( #args_name_stripped: #ref_tok #args_name )
}
})
.collect::<Vec<_>>()
})
.collect::<Vec<_>>()
};
let args_name_pattern = make_args_name_pattern(None);
let args_name_pattern_ref = make_args_name_pattern(Some(quote::quote!(ref)));
let args_type = methods
.iter()
.map(|method| method.args.iter().map(|(_, _, type_)| type_.clone()).collect::<Vec<_>>())
.collect::<Vec<_>>();
let args_compact_attr = methods.iter().map(|method| {
method
.args
.iter()
.map(|(is_compact, _, type_)| {
if *is_compact {
quote::quote_spanned!(type_.span() => #[codec(compact)] )
} else {
quote::quote!()
}
})
.collect::<Vec<_>>()
});
let default_docs =
[syn::parse_quote!(r"Contains a variant per dispatchable extrinsic that this pallet has.")];
let docs = if docs.is_empty() { &default_docs[..] } else { &docs[..] };
let maybe_compile_error = if def.call.is_none() {
quote::quote! {
compile_error!(concat!(
"`",
stringify!($pezpallet_name),
"` does not have #[pallet::call] defined, perhaps you should remove `Call` from \
construct_runtime?",
));
}
} else {
proc_macro2::TokenStream::new()
};
let count = COUNTER.with(|counter| counter.borrow_mut().inc());
let macro_ident = syn::Ident::new(&format!("__is_call_part_defined_{}", count), span);
let capture_docs = if cfg!(feature = "no-metadata-docs") { "never" } else { "always" };
// Wrap all calls inside of storage layers
if let Some(call) = def.call.as_ref() {
let item_impl =
&mut def.item.content.as_mut().expect("Checked by def parser").1[call.index];
let syn::Item::Impl(item_impl) = item_impl else {
unreachable!("Checked by def parser");
};
item_impl.items.iter_mut().enumerate().for_each(|(i, item)| {
if let syn::ImplItem::Fn(method) = item {
let return_type =
&call.methods.get(i).expect("def should be consistent with item").return_type;
let (ok_type, err_type) = match return_type {
CallReturnType::DispatchResult => (
quote::quote!(()),
quote::quote!(#pezframe_support::pezpallet_prelude::DispatchError),
),
CallReturnType::DispatchResultWithPostInfo => (
quote::quote!(#pezframe_support::dispatch::PostDispatchInfo),
quote::quote!(#pezframe_support::dispatch::DispatchErrorWithPostInfo),
),
};
let block = &method.block;
method.block = syn::parse_quote! {{
// We execute all dispatchable in a new storage layer, allowing them
// to return an error at any point, and undoing any storage changes.
#pezframe_support::storage::with_storage_layer::<#ok_type, #err_type, _>(
|| #block
)
}};
}
});
}
// Extracts #[allow] attributes, necessary so that we don't run into compiler warnings
let maybe_allow_attrs = methods
.iter()
.map(|method| {
let attrs = extract_or_return_allow_attrs(&method.attrs);
quote::quote! {
#(#attrs)*
}
})
.collect::<Vec<_>>();
let cfg_attrs = methods
.iter()
.map(|method| {
let attrs =
method.cfg_attrs.iter().map(|attr| attr.to_token_stream()).collect::<Vec<_>>();
quote::quote!( #( #attrs )* )
})
.collect::<Vec<_>>();
let feeless_checks = methods.iter().map(|method| &method.feeless_check).collect::<Vec<_>>();
let feeless_check =
feeless_checks.iter().zip(args_name.iter()).map(|(feeless_check, arg_name)| {
if let Some(check) = feeless_check {
quote::quote_spanned!(span => #check)
} else {
quote::quote_spanned!(span => |_origin, #( #arg_name, )*| { false })
}
});
let deprecation = match crate::deprecation::get_deprecation_enum(
&quote::quote! {#pezframe_support},
methods.iter().map(|item| (item.call_index as u8, item.attrs.as_ref())),
) {
Ok(deprecation) => deprecation,
Err(e) => return e.into_compile_error(),
};
// Implementation of the authorize function for each call
// `authorize_fn_pallet_impl` writes the user-defined authorize function as a function
// implementation for the pallet.
// `authorize_impl` is the call to this former function to implement `Authorize` trait.
let (authorize_fn_pallet_impl, authorize_impl) = methods
.iter()
.zip(args_name.iter())
.zip(args_type.iter())
.zip(cfg_attrs.iter())
.map(|(((method, arg_name), arg_type), cfg_attr)| {
if let Some(authorize_def) = &method.authorize {
let authorize_fn = &authorize_def.expr;
let attr_fn_getter = syn::Ident::new(
&format!("__macro_inner_authorize_call_for_{}", method.name),
authorize_fn.span(),
);
let source = syn::Ident::new("source", span);
let authorize_fn_pallet_impl = quote::quote_spanned!(authorize_fn.span() =>
// Closure don't have a writable type. So we fix the authorize token stream to
// be any implementation of a specific function.
// This allows to have good type inference on the closure.
//
// Then we wrap this into an implementation for `Pallet` in order to get access
// to `Self` as `Pallet` instead of `Call`.
#cfg_attr
impl<#type_impl_gen> Pallet<#type_use_gen> #where_clause {
#[doc(hidden)]
fn #attr_fn_getter() -> impl Fn(
#pezframe_support::pezpallet_prelude::TransactionSource,
#( &#arg_type ),*
) -> #pezframe_support::pezpallet_prelude::TransactionValidityWithRefund {
#authorize_fn
}
}
);
// `source` is from outside this block, so we can't use the authorize_fn span.
let authorize_impl = quote::quote!(
{
let authorize_fn = Pallet::<#type_use_gen>::#attr_fn_getter();
let res = authorize_fn(#source, #( #arg_name, )*);
Some(res)
}
);
(authorize_fn_pallet_impl, authorize_impl)
} else {
(Default::default(), quote::quote!(None))
}
})
.unzip::<_, _, Vec<TokenStream2>, Vec<TokenStream2>>();
// Implementation of the authorize function weight for each call
let mut authorize_fn_weight = Vec::<TokenStream2>::new();
for method in &methods {
let w = match &method.authorize {
Some(authorize_def) => expand_weight(
"authorize_",
pezframe_support,
def.dev_mode,
&mut weight_warnings,
method,
&authorize_def.weight,
),
// No authorize logic, weight is negligible
None => quote::quote!(#pezframe_support::pezpallet_prelude::Weight::zero()),
};
authorize_fn_weight.push(w);
}
assert_eq!(authorize_fn_weight.len(), methods.len());
quote::quote_spanned!(span =>
#[doc(hidden)]
mod warnings {
#(
#call_index_warnings
)*
#(
#weight_warnings
)*
}
#[allow(unused_imports)]
#[doc(hidden)]
pub mod __bizinikiwi_call_check {
#[macro_export]
#[doc(hidden)]
macro_rules! #macro_ident {
($pezpallet_name:ident) => {
#maybe_compile_error
};
}
#[doc(hidden)]
pub use #macro_ident as is_call_part_defined;
}
#( #[doc = #docs] )*
#[derive(
#pezframe_support::RuntimeDebugNoBound,
#pezframe_support::CloneNoBound,
#pezframe_support::EqNoBound,
#pezframe_support::PartialEqNoBound,
#pezframe_support::__private::codec::Encode,
#pezframe_support::__private::codec::Decode,
#pezframe_support::__private::codec::DecodeWithMemTracking,
#pezframe_support::__private::scale_info::TypeInfo,
)]
#[codec(encode_bound())]
#[codec(decode_bound())]
#[scale_info(skip_type_params(#type_use_gen), capture_docs = #capture_docs)]
#[allow(non_camel_case_types)]
pub enum #call_ident<#type_decl_bounded_gen> #where_clause {
#[doc(hidden)]
#[codec(skip)]
__Ignore(
::core::marker::PhantomData<(#type_use_gen,)>,
#pezframe_support::Never,
),
#(
#cfg_attrs
#( #[doc = #fn_doc] )*
#[codec(index = #call_index)]
#fn_name {
#(
#[allow(missing_docs)]
#args_compact_attr #args_name_stripped: #args_type
),*
},
)*
}
impl<#type_impl_gen> #call_ident<#type_use_gen> #where_clause {
#(
#cfg_attrs
#[doc = #new_call_variant_doc]
pub fn #new_call_variant_fn_name(
#( #args_name_stripped: #args_type ),*
) -> Self {
Self::#fn_name {
#( #args_name_stripped ),*
}
}
)*
}
impl<#type_impl_gen> #pezframe_support::dispatch::GetDispatchInfo
for #call_ident<#type_use_gen>
#where_clause
{
fn get_dispatch_info(&self) -> #pezframe_support::dispatch::DispatchInfo {
match *self {
#(
#cfg_attrs
Self::#fn_name { #( #args_name_pattern_ref, )* } => {
let __pallet_base_weight = #fn_weight;
let __pallet_weight = <
dyn #pezframe_support::dispatch::WeighData<( #( & #args_type, )* )>
>::weigh_data(&__pallet_base_weight, ( #( #args_name, )* ));
let __pallet_class = <
dyn #pezframe_support::dispatch::ClassifyDispatch<
( #( & #args_type, )* )
>
>::classify_dispatch(&__pallet_base_weight, ( #( #args_name, )* ));
let __pallet_pays_fee = <
dyn #pezframe_support::dispatch::PaysFee<( #( & #args_type, )* )>
>::pays_fee(&__pallet_base_weight, ( #( #args_name, )* ));
#pezframe_support::dispatch::DispatchInfo {
call_weight: __pallet_weight,
extension_weight: Default::default(),
class: __pallet_class,
pays_fee: __pallet_pays_fee,
}
},
)*
Self::__Ignore(_, _) => unreachable!("__Ignore cannot be used"),
}
}
}
impl<#type_impl_gen> #pezframe_support::dispatch::CheckIfFeeless for #call_ident<#type_use_gen>
#where_clause
{
type Origin = #pezframe_system::pezpallet_prelude::OriginFor<T>;
#[allow(unused_variables)]
fn is_feeless(&self, origin: &Self::Origin) -> bool {
match *self {
#(
#cfg_attrs
Self::#fn_name { #( #args_name_pattern_ref, )* } => {
let feeless_check = #feeless_check;
feeless_check(origin, #( #args_name, )*)
},
)*
Self::__Ignore(_, _) => unreachable!("__Ignore cannot be used"),
}
}
}
impl<#type_impl_gen> #pezframe_support::traits::GetCallName for #call_ident<#type_use_gen>
#where_clause
{
fn get_call_name(&self) -> &'static str {
match *self {
#( #cfg_attrs Self::#fn_name { .. } => stringify!(#fn_name), )*
Self::__Ignore(_, _) => unreachable!("__PhantomItem cannot be used."),
}
}
fn get_call_names() -> &'static [&'static str] {
&[ #( #cfg_attrs stringify!(#fn_name), )* ]
}
}
impl<#type_impl_gen> #pezframe_support::traits::GetCallIndex for #call_ident<#type_use_gen>
#where_clause
{
fn get_call_index(&self) -> u8 {
match *self {
#( #cfg_attrs Self::#fn_name { .. } => #call_index, )*
Self::__Ignore(_, _) => unreachable!("__PhantomItem cannot be used."),
}
}
fn get_call_indices() -> &'static [u8] {
&[ #( #cfg_attrs #call_index, )* ]
}
}
impl<#type_impl_gen> #pezframe_support::traits::UnfilteredDispatchable
for #call_ident<#type_use_gen>
#where_clause
{
type RuntimeOrigin = #pezframe_system::pezpallet_prelude::OriginFor<T>;
fn dispatch_bypass_filter(
self,
origin: Self::RuntimeOrigin
) -> #pezframe_support::dispatch::DispatchResultWithPostInfo {
#pezframe_support::dispatch_context::run_in_context(|| {
match self {
#(
#cfg_attrs
Self::#fn_name { #( #args_name_pattern, )* } => {
#pezframe_support::__private::pezsp_tracing::enter_span!(
#pezframe_support::__private::pezsp_tracing::trace_span!(stringify!(#fn_name))
);
#maybe_allow_attrs
#[allow(clippy::useless_conversion)]
<#pezpallet_ident<#type_use_gen>>::#fn_name(origin, #( #args_name, )* )
.map(Into::into).map_err(Into::into)
},
)*
Self::__Ignore(_, _) => {
let _ = origin; // Use origin for empty Call enum
unreachable!("__PhantomItem cannot be used.");
},
}
})
}
}
impl<#type_impl_gen> #pezframe_support::dispatch::Callable<T> for #pezpallet_ident<#type_use_gen>
#where_clause
{
type RuntimeCall = #call_ident<#type_use_gen>;
}
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen> #where_clause {
#[allow(dead_code)]
#[doc(hidden)]
pub fn call_functions() -> #pezframe_support::__private::metadata_ir::PalletCallMetadataIR {
#pezframe_support::__private::metadata_ir::PalletCallMetadataIR {
ty: #pezframe_support::__private::scale_info::meta_type::<#call_ident<#type_use_gen>>(),
deprecation_info: #deprecation,
}
}
}
#( #authorize_fn_pallet_impl )*
impl<#type_impl_gen> #pezframe_support::traits::Authorize for #call_ident<#type_use_gen>
#where_clause
{
fn authorize(&self, source: #pezframe_support::pezpallet_prelude::TransactionSource) -> ::core::option::Option<::core::result::Result<
(
#pezframe_support::pezpallet_prelude::ValidTransaction,
#pezframe_support::pezpallet_prelude::Weight,
),
#pezframe_support::pezpallet_prelude::TransactionValidityError
>>
{
match *self {
#(
#cfg_attrs
Self::#fn_name { #( #args_name_pattern_ref, )* } => {
#authorize_impl
},
)*
Self::__Ignore(_, _) => {
let _ = source;
unreachable!("__Ignore cannot be used")
},
}
}
fn weight_of_authorize(&self) -> #pezframe_support::pezpallet_prelude::Weight {
match *self {
#(
#cfg_attrs
Self::#fn_name { #( #args_name_pattern_ref, )* } => {
#authorize_fn_weight
},
)*
Self::__Ignore(_, _) => unreachable!("__Ignore cannot be used"),
}
}
}
)
}
@@ -0,0 +1,40 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::pallet::Def;
use proc_macro2::TokenStream;
/// Expands `composite_enum` and adds the `VariantCount` implementation for it.
pub fn expand_composites(def: &mut Def) -> TokenStream {
let mut expand = quote::quote!();
let pezframe_support = &def.pezframe_support;
for composite in &def.composites {
let name = &composite.ident;
let (impl_generics, ty_generics, where_clause) = composite.generics.split_for_impl();
let variants_count = composite.variant_count;
// add `VariantCount` implementation for `composite_enum`
expand.extend(quote::quote_spanned!(composite.attr_span =>
impl #impl_generics #pezframe_support::traits::VariantCount for #name #ty_generics #where_clause {
const VARIANT_COUNT: u32 = #variants_count;
}
));
}
expand
}
@@ -0,0 +1,170 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::pallet::{parse::GenericKind, Def};
use proc_macro2::TokenStream;
use quote::quote;
use syn::{parse_quote, Item};
///
/// * Generate default rust doc
pub fn expand_config(def: &mut Def) -> TokenStream {
let config = &def.config;
let config_item = {
let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[config.index];
if let Item::Trait(item) = item {
item
} else {
unreachable!("Checked by config parser")
}
};
config_item.attrs.insert(
0,
parse_quote!(
#[doc = r"
Configuration trait of this pallet.
The main purpose of this trait is to act as an interface between this pallet and the runtime in
which it is embedded in. A type, function, or constant in this trait is essentially left to be
configured by the runtime that includes this pallet.
Consequently, a runtime that wants to include this pallet must implement this trait."
]
),
);
config_item.attrs.retain(|attr| !attr.path().is_ident("deprecated"));
// insert `pezframe_system::Config` supertrait with `RuntimeEvent: From<Event<Self>>` if neither
// associated type nor type bound is defined.
if let Some(event) = &def.event {
if !def.is_pezframe_system {
let pezframe_system = &def.pezframe_system;
// can't use `type_use_gen()` since it returns `T`, not `Self`
let event_use_gen = match event.gen_kind {
GenericKind::None => quote!(),
GenericKind::Config => quote::quote_spanned! {event.attr_span => Self},
GenericKind::ConfigAndInstance => {
quote::quote_spanned! {event.attr_span => Self, I}
},
};
let supertrait_with_event_bound = syn::parse2::<syn::TypeParamBound>(
quote! { #pezframe_system::Config<RuntimeEvent: From<Event<#event_use_gen>>> },
)
.expect("Parsing super trait doesn't fail; qed");
config_item.supertraits.push(supertrait_with_event_bound.into());
}
}
// we only emit `DefaultConfig` if there are trait items, so an empty `DefaultConfig` is
// impossible consequently.
match &config.default_sub_trait {
Some(default_sub_trait) if default_sub_trait.items.len() > 0 => {
let trait_items = &default_sub_trait
.items
.iter()
.map(|item| {
if item.1 {
if let syn::TraitItem::Type(item) = item.0.clone() {
let mut item = item.clone();
item.bounds.clear();
syn::TraitItem::Type(item)
} else {
item.0.clone()
}
} else {
item.0.clone()
}
})
.collect::<Vec<_>>();
let type_param_bounds = if default_sub_trait.has_system {
let system = &def.pezframe_system;
quote::quote!(: #system::DefaultConfig)
} else {
quote::quote!()
};
quote!(
/// Based on [`Config`]. Auto-generated by
/// [`#[pallet::config(with_default)]`](`pezframe_support::pezpallet_macros::config`).
/// Can be used in tandem with
/// [`#[register_default_config]`](`pezframe_support::register_default_config`) and
/// [`#[derive_impl]`](`pezframe_support::derive_impl`) to derive test config traits
/// based on existing pallet config traits in a safe and developer-friendly way.
///
/// See [here](`pezframe_support::pezpallet_macros::config`) for more information and caveats about
/// the auto-generated `DefaultConfig` trait and how it is generated.
pub trait DefaultConfig #type_param_bounds {
#(#trait_items)*
}
)
},
_ => quote!(),
}
}
/// Generate the metadata for the associated types of the config trait.
///
/// Implements the `pezpallet_associated_types_metadata` function for the pallet.
pub fn expand_config_metadata(def: &Def) -> proc_macro2::TokenStream {
let pezframe_support = &def.pezframe_support;
let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site());
let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site());
let pezpallet_ident = &def.pezpallet_struct.pallet;
let trait_use_gen = &def.trait_use_generics(proc_macro2::Span::call_site());
let mut where_clauses = vec![&def.config.where_clause];
where_clauses.extend(def.extra_constants.iter().map(|d| &d.where_clause));
let completed_where_clause = super::merge_where_clauses(&where_clauses);
let types = def.config.associated_types_metadata.iter().map(|metadata| {
let ident = &metadata.ident;
let span = ident.span();
let ident_str = ident.to_string();
let cfgs = &metadata.cfg;
let no_docs = vec![];
let doc = if cfg!(feature = "no-metadata-docs") { &no_docs } else { &metadata.doc };
quote::quote_spanned!(span => {
#( #cfgs ) *
#pezframe_support::__private::metadata_ir::PalletAssociatedTypeMetadataIR {
name: #ident_str,
ty: #pezframe_support::__private::scale_info::meta_type::<
<T as Config #trait_use_gen>::#ident
>(),
docs: #pezframe_support::__private::vec![ #( #doc ),* ],
}
})
});
quote::quote!(
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen> #completed_where_clause {
#[doc(hidden)]
pub fn pezpallet_associated_types_metadata()
-> #pezframe_support::__private::vec::Vec<#pezframe_support::__private::metadata_ir::PalletAssociatedTypeMetadataIR>
{
#pezframe_support::__private::vec![ #( #types ),* ]
}
}
)
}
@@ -0,0 +1,135 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{deprecation::extract_or_return_allow_attrs, pallet::Def};
struct ConstDef {
/// Name of the associated type.
pub ident: syn::Ident,
/// The type in Get, e.g. `u32` in `type Foo: Get<u32>;`, but `Self` is replaced by `T`
pub type_: syn::Type,
/// The doc associated
pub doc: Vec<syn::Expr>,
/// default_byte implementation
pub default_byte_impl: proc_macro2::TokenStream,
/// Constant name for Metadata (optional)
pub metadata_name: Option<syn::Ident>,
/// Deprecation_info:
pub deprecation_info: proc_macro2::TokenStream,
}
/// Implement the `pezpallet_constants_metadata` function for the pallet.
pub fn expand_constants(def: &mut Def) -> proc_macro2::TokenStream {
let pezframe_support = &def.pezframe_support;
let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site());
let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site());
let pezpallet_ident = &def.pezpallet_struct.pallet;
let trait_use_gen = &def.trait_use_generics(proc_macro2::Span::call_site());
let mut where_clauses = vec![&def.config.where_clause];
where_clauses.extend(def.extra_constants.iter().map(|d| &d.where_clause));
let completed_where_clause = super::merge_where_clauses(&where_clauses);
let mut config_consts = vec![];
for const_ in def.config.consts_metadata.iter() {
let ident = &const_.ident;
let const_type = &const_.type_;
let deprecation_info = match crate::deprecation::get_deprecation(
&quote::quote! { #pezframe_support },
&const_.attrs,
) {
Ok(deprecation) => deprecation,
Err(e) => return e.into_compile_error(),
};
// Extracts #[allow] attributes, necessary so that we don't run into compiler warnings
let maybe_allow_attrs = extract_or_return_allow_attrs(&const_.attrs);
config_consts.push(ConstDef {
ident: const_.ident.clone(),
type_: const_.type_.clone(),
doc: const_.doc.clone(),
default_byte_impl: quote::quote!(
#(#maybe_allow_attrs)*
let value = <<T as Config #trait_use_gen>::#ident as
#pezframe_support::traits::Get<#const_type>>::get();
#pezframe_support::__private::codec::Encode::encode(&value)
),
metadata_name: None,
deprecation_info,
})
}
let mut extra_consts = vec![];
for const_ in def.extra_constants.iter().flat_map(|d| &d.extra_constants) {
let ident = &const_.ident;
let deprecation_info = match crate::deprecation::get_deprecation(
&quote::quote! { #pezframe_support },
&const_.attrs,
) {
Ok(deprecation) => deprecation,
Err(e) => return e.into_compile_error(),
};
// Extracts #[allow] attributes, necessary so that we don't run into compiler warnings
let maybe_allow_attrs = extract_or_return_allow_attrs(&const_.attrs);
extra_consts.push(ConstDef {
ident: const_.ident.clone(),
type_: const_.type_.clone(),
doc: const_.doc.clone(),
default_byte_impl: quote::quote!(
#(#maybe_allow_attrs)*
let value = <Pallet<#type_use_gen>>::#ident();
#pezframe_support::__private::codec::Encode::encode(&value)
),
metadata_name: const_.metadata_name.clone(),
deprecation_info,
})
}
let consts = config_consts.into_iter().chain(extra_consts.into_iter()).map(|const_| {
let const_type = &const_.type_;
let ident_str = format!("{}", const_.metadata_name.unwrap_or(const_.ident));
let no_docs = vec![];
let doc = if cfg!(feature = "no-metadata-docs") { &no_docs } else { &const_.doc };
let default_byte_impl = &const_.default_byte_impl;
let deprecation_info = &const_.deprecation_info;
quote::quote!({
#pezframe_support::__private::metadata_ir::PalletConstantMetadataIR {
name: #ident_str,
ty: #pezframe_support::__private::scale_info::meta_type::<#const_type>(),
value: { #default_byte_impl },
docs: #pezframe_support::__private::vec![ #( #doc ),* ],
deprecation_info: #deprecation_info
}
})
});
quote::quote!(
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen> #completed_where_clause{
#[doc(hidden)]
pub fn pezpallet_constants_metadata()
-> #pezframe_support::__private::Vec<#pezframe_support::__private::metadata_ir::PalletConstantMetadataIR>
{
#pezframe_support::__private::vec![ #( #consts ),* ]
}
}
)
}
@@ -0,0 +1,103 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use proc_macro2::Span;
use crate::pallet::Def;
pub fn expand_doc_only(def: &mut Def) -> proc_macro2::TokenStream {
let dispatchables = if let Some(call_def) = &def.call {
let type_impl_generics = def.type_impl_generics(Span::call_site());
call_def
.methods
.iter()
.map(|method| {
let name = &method.name;
let args = &method
.args
.iter()
.map(|(_, arg_name, arg_type)| quote::quote!( #arg_name: #arg_type, ))
.collect::<proc_macro2::TokenStream>();
let docs = &method.docs;
let real = format!(" [`Pallet::{}`].", name);
quote::quote!(
#( #[doc = #docs] )*
///
/// # Warning: Doc-Only
///
/// This function is an automatically generated, and is doc-only, uncallable
/// stub. See the real version in
#[ doc = #real ]
pub fn #name<#type_impl_generics>(#args) { unreachable!(); }
)
})
.collect::<proc_macro2::TokenStream>()
} else {
quote::quote!()
};
let storage_types = def
.storages
.iter()
.map(|storage| {
let storage_name = &storage.ident;
let storage_type_docs = &storage.docs;
let real = format!("[`pallet::{}`].", storage_name);
quote::quote!(
#( #[doc = #storage_type_docs] )*
///
/// # Warning: Doc-Only
///
/// This type is automatically generated, and is doc-only. See the real version in
#[ doc = #real ]
pub struct #storage_name();
)
})
.collect::<proc_macro2::TokenStream>();
quote::quote!(
/// Auto-generated docs-only module listing all (public and private) defined storage types
/// for this pallet.
///
/// # Warning: Doc-Only
///
/// Members of this module cannot be used directly and are only provided for documentation
/// purposes.
///
/// To see the actual storage type, find a struct with the same name at the root of the
/// pallet, in the list of [*Type Definitions*](../index.html#types).
#[cfg(doc)]
pub mod storage_types {
use super::*;
#storage_types
}
/// Auto-generated docs-only module listing all defined dispatchables for this pallet.
///
/// # Warning: Doc-Only
///
/// Members of this module cannot be used directly and are only provided for documentation
/// purposes. To see the real version of each dispatchable, look for them in [`Pallet`] or
/// [`Call`].
#[cfg(doc)]
pub mod dispatchables {
use super::*;
#dispatchables
}
)
}
@@ -0,0 +1,172 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::pallet::Def;
use proc_macro2::TokenStream;
use quote::ToTokens;
use syn::{spanned::Spanned, Attribute, Lit, LitStr};
const DOC: &'static str = "doc";
const PALLET_DOC: &'static str = "pezpallet_doc";
/// Get the documentation file path from the `pezpallet_doc` attribute.
///
/// Supported format:
/// `#[pezpallet_doc(PATH)]`: The path of the file from which the documentation is loaded
fn parse_pallet_doc_value(attr: &Attribute) -> syn::Result<DocMetaValue> {
let lit: syn::LitStr = attr.parse_args().map_err(|_| {
let msg = "The `pezpallet_doc` received an unsupported argument. Supported format: `pezpallet_doc(\"PATH\")`";
syn::Error::new(attr.span(), msg)
})?;
Ok(DocMetaValue::Path(lit))
}
/// Get the value from the `doc` comment attribute:
///
/// Supported formats:
/// - `#[doc = "A doc string"]`: Documentation as a string literal
/// - `#[doc = include_str!(PATH)]`: Documentation obtained from a path
fn parse_doc_value(attr: &Attribute) -> syn::Result<Option<DocMetaValue>> {
if !attr.path().is_ident(DOC) {
return Ok(None);
}
let meta = attr.meta.require_name_value()?;
match &meta.value {
syn::Expr::Lit(lit) => Ok(Some(DocMetaValue::Lit(lit.lit.clone()))),
syn::Expr::Macro(mac) if mac.mac.path.is_ident("include_str") =>
Ok(Some(DocMetaValue::Path(mac.mac.parse_body()?))),
_ =>
Err(syn::Error::new(attr.span(), "Expected `= \"docs\"` or `= include_str!(\"PATH\")`")),
}
}
/// Supported documentation tokens.
#[derive(Debug)]
enum DocMetaValue {
/// Documentation with string literals.
///
/// `#[doc = "Lit"]`
Lit(Lit),
/// Documentation with `include_str!` macro.
///
/// The string literal represents the file `PATH`.
///
/// `#[doc = include_str!(PATH)]`
Path(LitStr),
}
impl ToTokens for DocMetaValue {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
DocMetaValue::Lit(lit) => lit.to_tokens(tokens),
DocMetaValue::Path(path_lit) => {
let decl = quote::quote!(include_str!(#path_lit));
tokens.extend(decl)
},
}
}
}
/// Extract the documentation from the given pallet definition
/// to include in the runtime metadata.
///
/// Implement a `pezpallet_documentation_metadata` function to fetch the
/// documentation that is included in the metadata.
///
/// The documentation is placed on the pallet similar to:
///
/// ```ignore
/// #[pallet]
/// /// Documentation for pallet
/// #[doc = "Documentation for pallet"]
/// #[doc = include_str!("../README.md")]
/// #[pezpallet_doc("../documentation1.md")]
/// #[pezpallet_doc("../documentation2.md")]
/// pub mod pallet {}
/// ```
///
/// # pezpallet_doc
///
/// The `pezpallet_doc` attribute can only be provided with one argument,
/// which is the file path that holds the documentation to be added to the metadata.
///
/// Unlike the `doc` attribute, the documentation provided to the `proc_macro` attribute is
/// not added to the pallet.
pub fn expand_documentation(def: &mut Def) -> proc_macro2::TokenStream {
let pezframe_support = &def.pezframe_support;
let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site());
let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site());
let pezpallet_ident = &def.pezpallet_struct.pallet;
let where_clauses = &def.config.where_clause;
// TODO: Use [drain_filter](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.drain_filter) when it is stable.
// The `pezpallet_doc` attributes are excluded from the generation of the pallet,
// but they are included in the runtime metadata.
let mut pezpallet_docs = Vec::with_capacity(def.item.attrs.len());
let mut index = 0;
while index < def.item.attrs.len() {
let attr = &def.item.attrs[index];
if attr.path().get_ident().map_or(false, |i| *i == PALLET_DOC) {
pezpallet_docs.push(def.item.attrs.remove(index));
// Do not increment the index, we have just removed the
// element from the attributes.
continue;
}
index += 1;
}
// Capture the `#[doc = include_str!("../README.md")]` and `#[doc = "Documentation"]`.
let docs = match def
.item
.attrs
.iter()
.filter_map(|v| parse_doc_value(v).transpose())
.collect::<syn::Result<Vec<_>>>()
{
Ok(r) => r,
Err(err) => return err.into_compile_error(),
};
// Capture the `#[pezpallet_doc("../README.md")]`.
let pezpallet_docs = match pezpallet_docs
.into_iter()
.map(|attr| parse_pallet_doc_value(&attr))
.collect::<syn::Result<Vec<_>>>()
{
Ok(docs) => docs,
Err(err) => return err.into_compile_error(),
};
let docs = docs.iter().chain(pezpallet_docs.iter());
quote::quote!(
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen> #where_clauses{
#[doc(hidden)]
pub fn pezpallet_documentation_metadata()
-> #pezframe_support::__private::Vec<&'static str>
{
#pezframe_support::__private::vec![ #( #docs ),* ]
}
}
)
}
@@ -0,0 +1,227 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
deprecation::extract_or_return_allow_attrs,
pallet::{
parse::error::{VariantDef, VariantField},
Def,
},
COUNTER,
};
use pezframe_support_procedural_tools::get_doc_literals;
use quote::ToTokens;
use syn::spanned::Spanned;
///
/// * impl various trait on Error
pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream {
let count = COUNTER.with(|counter| counter.borrow_mut().inc());
let error_token_unique_id =
syn::Ident::new(&format!("__tt_error_token_{}", count), def.item.span());
let pezframe_support = &def.pezframe_support;
let pezframe_system = &def.pezframe_system;
let config_where_clause = &def.config.where_clause;
let error = if let Some(error) = &def.error {
error
} else {
return quote::quote! {
#[macro_export]
#[doc(hidden)]
macro_rules! #error_token_unique_id {
{
$caller:tt
your_tt_return = [{ $my_tt_return:path }]
} => {
$my_tt_return! {
$caller
}
};
}
pub use #error_token_unique_id as tt_error_token;
};
};
let error_ident = &error.error;
let type_impl_gen = &def.type_impl_generics(error.attr_span);
let type_use_gen = &def.type_use_generics(error.attr_span);
let phantom_variant: syn::Variant = syn::parse_quote!(
#[doc(hidden)]
#[codec(skip)]
__Ignore(
core::marker::PhantomData<(#type_use_gen)>,
#pezframe_support::Never,
)
);
let as_str_matches =
error
.variants
.iter()
.map(|VariantDef { ident: variant, field: field_ty, cfg_attrs, maybe_allow_attrs }| {
let variant_str = variant.to_string();
let cfg_attrs = cfg_attrs.iter().map(|attr| attr.to_token_stream());
match field_ty {
Some(VariantField { is_named: true }) => {
quote::quote_spanned!(error.attr_span => #( #cfg_attrs )* #(#maybe_allow_attrs)* Self::#variant { .. } => #variant_str,)
},
Some(VariantField { is_named: false }) => {
quote::quote_spanned!(error.attr_span => #( #cfg_attrs )* #(#maybe_allow_attrs)* Self::#variant(..) => #variant_str,)
},
None => {
quote::quote_spanned!(error.attr_span => #( #cfg_attrs )* #(#maybe_allow_attrs)* Self::#variant => #variant_str,)
},
}
});
let error_item = {
let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[error.index];
if let syn::Item::Enum(item) = item {
item
} else {
unreachable!("Checked by error parser")
}
};
error_item.variants.insert(0, phantom_variant);
let capture_docs = if cfg!(feature = "no-metadata-docs") { "never" } else { "always" };
let deprecation = match crate::deprecation::get_deprecation_enum(
&quote::quote! {#pezframe_support},
error_item.variants.iter().enumerate().map(|(index, item)| {
let index = crate::deprecation::variant_index_for_deprecation(index as u8, item);
(index, item.attrs.as_ref())
}),
) {
Ok(deprecation) => deprecation,
Err(e) => return e.into_compile_error(),
};
// derive TypeInfo for error metadata
error_item.attrs.push(syn::parse_quote! {
#[derive(
#pezframe_support::__private::codec::Encode,
#pezframe_support::__private::codec::Decode,
#pezframe_support::__private::codec::DecodeWithMemTracking,
#pezframe_support::__private::scale_info::TypeInfo,
#pezframe_support::PalletError,
)]
});
error_item.attrs.push(syn::parse_quote!(
#[scale_info(skip_type_params(#type_use_gen), capture_docs = #capture_docs)]
));
if get_doc_literals(&error_item.attrs).is_empty() {
error_item.attrs.push(syn::parse_quote!(
#[doc = "The `Error` enum of this pallet."]
));
}
// Extracts #[allow] attributes, necessary so that we don't run into compiler warnings
let maybe_allow_attrs: Vec<syn::Attribute> =
extract_or_return_allow_attrs(&error_item.attrs).collect();
quote::quote_spanned!(error.attr_span =>
#(#maybe_allow_attrs)*
impl<#type_impl_gen> core::fmt::Debug for #error_ident<#type_use_gen>
#config_where_clause
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>)
-> core::fmt::Result
{
f.write_str(self.as_str())
}
}
#(#maybe_allow_attrs)*
impl<#type_impl_gen> #error_ident<#type_use_gen> #config_where_clause {
#[doc(hidden)]
pub fn as_str(&self) -> &'static str {
match &self {
#(#maybe_allow_attrs)* Self::__Ignore(_, _) => unreachable!("`__Ignore` can never be constructed"),
#( #as_str_matches )*
}
}
}
#(#maybe_allow_attrs)*
impl<#type_impl_gen> From<#error_ident<#type_use_gen>> for &'static str
#config_where_clause
{
fn from(err: #error_ident<#type_use_gen>) -> &'static str {
err.as_str()
}
}
#(#maybe_allow_attrs)*
impl<#type_impl_gen> From<#error_ident<#type_use_gen>>
for #pezframe_support::pezsp_runtime::DispatchError
#config_where_clause
{
fn from(err: #error_ident<#type_use_gen>) -> Self {
use #pezframe_support::__private::codec::Encode;
let index = <
<T as #pezframe_system::Config>::PalletInfo
as #pezframe_support::traits::PalletInfo
>::index::<Pallet<#type_use_gen>>()
.expect("Every active module has an index in the runtime; qed") as u8;
let mut encoded = err.encode();
encoded.resize(#pezframe_support::MAX_MODULE_ERROR_ENCODED_SIZE, 0);
#pezframe_support::pezsp_runtime::DispatchError::Module(#pezframe_support::pezsp_runtime::ModuleError {
index,
error: TryInto::try_into(encoded).expect("encoded error is resized to be equal to the maximum encoded error size; qed"),
message: Some(err.as_str()),
})
}
}
#[macro_export]
#[doc(hidden)]
macro_rules! #error_token_unique_id {
{
$caller:tt
your_tt_return = [{ $my_tt_return:path }]
} => {
$my_tt_return! {
$caller
error = [{ #error_ident }]
}
};
}
pub use #error_token_unique_id as tt_error_token;
#(#maybe_allow_attrs)*
impl<#type_impl_gen> #error_ident<#type_use_gen> #config_where_clause {
#[allow(dead_code)]
#[doc(hidden)]
pub fn error_metadata() -> #pezframe_support::__private::metadata_ir::PalletErrorMetadataIR {
#(#maybe_allow_attrs)*
#pezframe_support::__private::metadata_ir::PalletErrorMetadataIR {
ty: #pezframe_support::__private::scale_info::meta_type::<#error_ident<#type_use_gen>>(),
deprecation_info: #deprecation,
}
}
}
)
}
@@ -0,0 +1,204 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
deprecation::extract_or_return_allow_attrs,
pallet::{parse::event::PalletEventDepositAttr, Def},
COUNTER,
};
use pezframe_support_procedural_tools::get_doc_literals;
use syn::{spanned::Spanned, Ident};
///
/// * Add __Ignore variant on Event
/// * Impl various trait on Event including metadata
/// * if deposit_event is defined, implement deposit_event on module.
pub fn expand_event(def: &mut Def) -> proc_macro2::TokenStream {
let count = COUNTER.with(|counter| counter.borrow_mut().inc());
let (event, macro_ident) = if let Some(event) = &def.event {
let ident = Ident::new(&format!("__is_event_part_defined_{}", count), event.attr_span);
(event, ident)
} else {
let macro_ident =
Ident::new(&format!("__is_event_part_defined_{}", count), def.item.span());
return quote::quote! {
#[doc(hidden)]
pub mod __bizinikiwi_event_check {
#[macro_export]
#[doc(hidden)]
macro_rules! #macro_ident {
($pezpallet_name:ident) => {
compile_error!(concat!(
"`",
stringify!($pezpallet_name),
"` does not have #[pallet::event] defined, perhaps you should \
remove `Event` from construct_runtime?",
));
}
}
#[doc(hidden)]
pub use #macro_ident as is_event_part_defined;
}
};
};
let event_where_clause = &event.where_clause;
// NOTE: actually event where clause must be a subset of config where clause because of
// `type RuntimeEvent: From<Event<Self>>`. But we merge either way for potential better error
// message
let completed_where_clause =
super::merge_where_clauses(&[&event.where_clause, &def.config.where_clause]);
let event_ident = &event.event;
let pezframe_system = &def.pezframe_system;
let pezframe_support = &def.pezframe_support;
let event_use_gen = &event.gen_kind.type_use_gen(event.attr_span);
let event_impl_gen = &event.gen_kind.type_impl_gen(event.attr_span);
let event_item = {
let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[event.index];
if let syn::Item::Enum(item) = item {
item
} else {
unreachable!("Checked by event parser")
}
};
// Phantom data is added for generic event.
if event.gen_kind.is_generic() {
let variant = syn::parse_quote!(
#[doc(hidden)]
#[codec(skip)]
__Ignore(
::core::marker::PhantomData<(#event_use_gen)>,
#pezframe_support::Never,
)
);
// Push ignore variant at the end.
event_item.variants.push(variant);
}
let deprecation = match crate::deprecation::get_deprecation_enum(
&quote::quote! {#pezframe_support},
event_item.variants.iter().enumerate().map(|(index, item)| {
let index = crate::deprecation::variant_index_for_deprecation(index as u8, item);
(index, item.attrs.as_ref())
}),
) {
Ok(deprecation) => deprecation,
Err(e) => return e.into_compile_error(),
};
if get_doc_literals(&event_item.attrs).is_empty() {
event_item
.attrs
.push(syn::parse_quote!(#[doc = "The `Event` enum of this pallet"]));
}
// derive some traits because system event require Clone, FullCodec, Eq, PartialEq and Debug
event_item.attrs.push(syn::parse_quote!(
#[derive(
#pezframe_support::CloneNoBound,
#pezframe_support::EqNoBound,
#pezframe_support::PartialEqNoBound,
#pezframe_support::DebugNoBound,
#pezframe_support::__private::codec::Encode,
#pezframe_support::__private::codec::Decode,
#pezframe_support::__private::codec::DecodeWithMemTracking,
#pezframe_support::__private::scale_info::TypeInfo,
)]
));
let capture_docs = if cfg!(feature = "no-metadata-docs") { "never" } else { "always" };
// skip requirement for type params to implement `TypeInfo`, and set docs capture
event_item.attrs.push(syn::parse_quote!(
#[scale_info(skip_type_params(#event_use_gen), capture_docs = #capture_docs)]
));
// Extracts #[allow] attributes, necessary so that we don't run into compiler warnings
let maybe_allow_attrs: Vec<syn::Attribute> =
extract_or_return_allow_attrs(&event_item.attrs).collect();
let deposit_event = if let Some(deposit_event) = &event.deposit_event {
let event_use_gen = &event.gen_kind.type_use_gen(event.attr_span);
let type_impl_gen = &def.type_impl_generics(event.attr_span);
let type_use_gen = &def.type_use_generics(event.attr_span);
let pezpallet_ident = &def.pezpallet_struct.pallet;
let PalletEventDepositAttr { fn_vis, fn_span, .. } = deposit_event;
quote::quote_spanned!(*fn_span =>
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen> #completed_where_clause {
#(#maybe_allow_attrs)*
#fn_vis fn deposit_event(event: Event<#event_use_gen>) {
let event = <
<T as #pezframe_system::Config>::RuntimeEvent as
From<Event<#event_use_gen>>
>::from(event);
let event = <
<T as #pezframe_system::Config>::RuntimeEvent as
Into<<T as #pezframe_system::Config>::RuntimeEvent>
>::into(event);
<#pezframe_system::Pallet<T>>::deposit_event(event)
}
}
)
} else {
Default::default()
};
quote::quote_spanned!(event.attr_span =>
#[doc(hidden)]
pub mod __bizinikiwi_event_check {
#[macro_export]
#[doc(hidden)]
macro_rules! #macro_ident {
($pezpallet_name:ident) => {};
}
#[doc(hidden)]
pub use #macro_ident as is_event_part_defined;
}
#deposit_event
#(#maybe_allow_attrs)*
impl<#event_impl_gen> From<#event_ident<#event_use_gen>> for () #event_where_clause {
fn from(_: #event_ident<#event_use_gen>) {}
}
#(#maybe_allow_attrs)*
impl<#event_impl_gen> #event_ident<#event_use_gen> #event_where_clause {
#[allow(dead_code)]
#[doc(hidden)]
pub fn event_metadata<W: #pezframe_support::__private::scale_info::TypeInfo + 'static>() -> #pezframe_support::__private::metadata_ir::PalletEventMetadataIR {
#pezframe_support::__private::metadata_ir::PalletEventMetadataIR {
ty: #pezframe_support::__private::scale_info::meta_type::<W>(),
deprecation_info: #deprecation,
}
}
}
)
}
@@ -0,0 +1,50 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::pallet::Def;
///
/// * implement the trait `pezsp_runtime::BuildStorage`
pub fn expand_genesis_build(def: &mut Def) -> proc_macro2::TokenStream {
let genesis_config = if let Some(genesis_config) = &def.genesis_config {
genesis_config
} else {
return Default::default();
};
let genesis_build = def.genesis_build.as_ref().expect("Checked by def parser");
let pezframe_support = &def.pezframe_support;
let type_impl_gen = &genesis_config.gen_kind.type_impl_gen(genesis_build.attr_span);
let gen_cfg_ident = &genesis_config.genesis_config;
let gen_cfg_use_gen = &genesis_config.gen_kind.type_use_gen(genesis_build.attr_span);
let where_clause = &genesis_build.where_clause;
quote::quote_spanned!(genesis_build.attr_span =>
#pezframe_support::std_enabled! {
impl<#type_impl_gen> #pezframe_support::pezsp_runtime::BuildStorage for #gen_cfg_ident<#gen_cfg_use_gen> #where_clause
{
fn assimilate_storage(&self, storage: &mut #pezframe_support::pezsp_runtime::Storage) -> std::result::Result<(), std::string::String> {
#pezframe_support::__private::BasicExternalities::execute_with_storage(storage, || {
self.build();
Ok(())
})
}
}
}
)
}
@@ -0,0 +1,147 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{pallet::Def, COUNTER};
use pezframe_support_procedural_tools::get_doc_literals;
use quote::ToTokens;
use syn::{spanned::Spanned, Ident};
///
/// * add various derive trait on GenesisConfig struct.
pub fn expand_genesis_config(def: &mut Def) -> proc_macro2::TokenStream {
let count = COUNTER.with(|counter| counter.borrow_mut().inc());
let (genesis_config, def_macro_ident, std_macro_ident) =
if let Some(genesis_config) = &def.genesis_config {
let def_macro_ident = Ident::new(
&format!("__is_genesis_config_defined_{}", count),
genesis_config.genesis_config.span(),
);
let std_macro_ident = Ident::new(
&format!("__is_std_macro_defined_for_genesis_{}", count),
genesis_config.genesis_config.span(),
);
(genesis_config, def_macro_ident, std_macro_ident)
} else {
let def_macro_ident =
Ident::new(&format!("__is_genesis_config_defined_{}", count), def.item.span());
let std_macro_ident =
Ident::new(&format!("__is_std_enabled_for_genesis_{}", count), def.item.span());
return quote::quote! {
#[doc(hidden)]
pub mod __bizinikiwi_genesis_config_check {
#[macro_export]
#[doc(hidden)]
macro_rules! #def_macro_ident {
($pezpallet_name:ident) => {
compile_error!(concat!(
"`",
stringify!($pezpallet_name),
"` does not have #[pallet::genesis_config] defined, perhaps you should \
remove `Config` from construct_runtime?",
));
}
}
#[macro_export]
#[doc(hidden)]
macro_rules! #std_macro_ident {
($pezpallet_name:ident, $pezpallet_path:expr) => {};
}
#[doc(hidden)]
pub use #def_macro_ident as is_genesis_config_defined;
#[doc(hidden)]
pub use #std_macro_ident as is_std_enabled_for_genesis;
}
};
};
let pezframe_support = &def.pezframe_support;
let genesis_config_item =
&mut def.item.content.as_mut().expect("Checked by def parser").1[genesis_config.index];
let serde_crate = format!("{}::__private::serde", pezframe_support.to_token_stream());
match genesis_config_item {
syn::Item::Enum(syn::ItemEnum { attrs, .. }) |
syn::Item::Struct(syn::ItemStruct { attrs, .. }) |
syn::Item::Type(syn::ItemType { attrs, .. }) => {
if get_doc_literals(attrs).is_empty() {
attrs.push(syn::parse_quote!(
#[doc = r"
Can be used to configure the
[genesis state](https://docs.pezkuwichain.io/build/genesis-configuration/)
of this pallet.
"]
));
}
attrs.push(syn::parse_quote!(
#[derive(#pezframe_support::Serialize, #pezframe_support::Deserialize)]
));
attrs.push(syn::parse_quote!( #[serde(rename_all = "camelCase")] ));
attrs.push(syn::parse_quote!( #[serde(deny_unknown_fields)] ));
attrs.push(syn::parse_quote!( #[serde(bound(serialize = ""))] ));
attrs.push(syn::parse_quote!( #[serde(bound(deserialize = ""))] ));
attrs.push(syn::parse_quote!( #[serde(crate = #serde_crate)] ));
},
_ => unreachable!("Checked by genesis_config parser"),
}
quote::quote! {
#[doc(hidden)]
pub mod __bizinikiwi_genesis_config_check {
#[macro_export]
#[doc(hidden)]
macro_rules! #def_macro_ident {
($pezpallet_name:ident) => {};
}
#[cfg(not(feature = "std"))]
#[macro_export]
#[doc(hidden)]
macro_rules! #std_macro_ident {
($pezpallet_name:ident, $pezpallet_path:expr) => {
compile_error!(concat!(
"`",
stringify!($pezpallet_name),
"` does not have the std feature enabled, this will cause the `",
$pezpallet_path,
"::GenesisConfig` type to not implement serde traits."
));
};
}
#[cfg(feature = "std")]
#[macro_export]
#[doc(hidden)]
macro_rules! #std_macro_ident {
($pezpallet_name:ident, $pezpallet_path:expr) => {};
}
#[doc(hidden)]
pub use #def_macro_ident as is_genesis_config_defined;
#[doc(hidden)]
pub use #std_macro_ident as is_std_enabled_for_genesis;
}
}
}
@@ -0,0 +1,339 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::pallet::Def;
/// * implement the individual traits using the Hooks trait
pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream {
let (where_clause, span, has_runtime_upgrade) = match def.hooks.as_ref() {
Some(hooks) => {
let where_clause = hooks.where_clause.clone();
let span = hooks.attr_span;
let has_runtime_upgrade = hooks.has_runtime_upgrade;
(where_clause, span, has_runtime_upgrade)
},
None => (def.config.where_clause.clone(), def.pezpallet_struct.attr_span, false),
};
let pezframe_support = &def.pezframe_support;
let type_impl_gen = &def.type_impl_generics(span);
let type_use_gen = &def.type_use_generics(span);
let pezpallet_ident = &def.pezpallet_struct.pallet;
let pezframe_system = &def.pezframe_system;
let pezpallet_name = quote::quote! {
<
<T as #pezframe_system::Config>::PalletInfo
as
#pezframe_support::traits::PalletInfo
>::name::<Self>().unwrap_or("<unknown pallet name>")
};
let initialize_on_chain_storage_version = if let Some(in_code_version) =
&def.pezpallet_struct.storage_version
{
quote::quote! {
#pezframe_support::__private::log::info!(
target: #pezframe_support::LOG_TARGET,
"🐥 New pallet {:?} detected in the runtime. Initializing the on-chain storage version to match the storage version defined in the pallet: {:?}",
#pezpallet_name,
#in_code_version
);
#in_code_version.put::<Self>();
}
} else {
quote::quote! {
let default_version = #pezframe_support::traits::StorageVersion::new(0);
#pezframe_support::__private::log::info!(
target: #pezframe_support::LOG_TARGET,
"🐥 New pallet {:?} detected in the runtime. The pallet has no defined storage version, so the on-chain version is being initialized to {:?}.",
#pezpallet_name,
default_version
);
default_version.put::<Self>();
}
};
let log_runtime_upgrade = if has_runtime_upgrade {
// a migration is defined here.
quote::quote! {
#pezframe_support::__private::log::info!(
target: #pezframe_support::LOG_TARGET,
"⚠️ {} declares internal migrations (which *might* execute). \
On-chain `{:?}` vs in-code storage version `{:?}`",
#pezpallet_name,
<Self as #pezframe_support::traits::GetStorageVersion>::on_chain_storage_version(),
<Self as #pezframe_support::traits::GetStorageVersion>::in_code_storage_version(),
);
}
} else {
// default.
quote::quote! {
#pezframe_support::__private::log::debug!(
target: #pezframe_support::LOG_TARGET,
"✅ no migration for {}",
#pezpallet_name,
);
}
};
let hooks_impl = if def.hooks.is_none() {
let pezframe_system = &def.pezframe_system;
quote::quote! {
impl<#type_impl_gen>
#pezframe_support::traits::Hooks<#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>>
for #pezpallet_ident<#type_use_gen> #where_clause {}
}
} else {
proc_macro2::TokenStream::new()
};
// If a storage version is set, we should ensure that the storage version on chain matches the
// in-code storage version. This assumes that `Executive` is running custom migrations before
// the pallets are called.
let post_storage_version_check = if def.pezpallet_struct.storage_version.is_some() {
quote::quote! {
let on_chain_version = <Self as #pezframe_support::traits::GetStorageVersion>::on_chain_storage_version();
let in_code_version = <Self as #pezframe_support::traits::GetStorageVersion>::in_code_storage_version();
if on_chain_version != in_code_version {
#pezframe_support::__private::log::error!(
target: #pezframe_support::LOG_TARGET,
"{}: On chain storage version {:?} doesn't match in-code storage version {:?}.",
#pezpallet_name,
on_chain_version,
in_code_version,
);
return Err("On chain and in-code storage version do not match. Missing runtime upgrade?".into());
}
}
} else {
quote::quote! {
let on_chain_version = <Self as #pezframe_support::traits::GetStorageVersion>::on_chain_storage_version();
if on_chain_version != #pezframe_support::traits::StorageVersion::new(0) {
#pezframe_support::__private::log::error!(
target: #pezframe_support::LOG_TARGET,
"{}: On chain storage version {:?} is set to non zero, \
while the pallet is missing the `#[pallet::storage_version(VERSION)]` attribute.",
#pezpallet_name,
on_chain_version,
);
return Err("On chain storage version set, while the pallet doesn't \
have the `#[pallet::storage_version(VERSION)]` attribute.".into());
}
}
};
quote::quote_spanned!(span =>
#hooks_impl
impl<#type_impl_gen>
#pezframe_support::traits::OnFinalize<#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>>
for #pezpallet_ident<#type_use_gen> #where_clause
{
fn on_finalize(n: #pezframe_system::pezpallet_prelude::BlockNumberFor::<T>) {
#pezframe_support::__private::pezsp_tracing::enter_span!(
#pezframe_support::__private::pezsp_tracing::trace_span!("on_finalize")
);
<
Self as #pezframe_support::traits::Hooks<
#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>
>
>::on_finalize(n)
}
}
impl<#type_impl_gen>
#pezframe_support::traits::OnIdle<#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>>
for #pezpallet_ident<#type_use_gen> #where_clause
{
fn on_idle(
n: #pezframe_system::pezpallet_prelude::BlockNumberFor::<T>,
remaining_weight: #pezframe_support::weights::Weight
) -> #pezframe_support::weights::Weight {
<
Self as #pezframe_support::traits::Hooks<
#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>
>
>::on_idle(n, remaining_weight)
}
}
impl<#type_impl_gen>
#pezframe_support::traits::OnPoll<#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>>
for #pezpallet_ident<#type_use_gen> #where_clause
{
fn on_poll(
n: #pezframe_system::pezpallet_prelude::BlockNumberFor::<T>,
weight: &mut #pezframe_support::weights::WeightMeter
) {
<
Self as #pezframe_support::traits::Hooks<
#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>
>
>::on_poll(n, weight);
}
}
impl<#type_impl_gen>
#pezframe_support::traits::OnInitialize<#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>>
for #pezpallet_ident<#type_use_gen> #where_clause
{
fn on_initialize(
n: #pezframe_system::pezpallet_prelude::BlockNumberFor::<T>
) -> #pezframe_support::weights::Weight {
#pezframe_support::__private::pezsp_tracing::enter_span!(
#pezframe_support::__private::pezsp_tracing::trace_span!("on_initialize")
);
<
Self as #pezframe_support::traits::Hooks<
#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>
>
>::on_initialize(n)
}
}
impl<#type_impl_gen>
#pezframe_support::traits::BeforeAllRuntimeMigrations
for #pezpallet_ident<#type_use_gen> #where_clause
{
fn before_all_runtime_migrations() -> #pezframe_support::weights::Weight {
use #pezframe_support::traits::{Get, PalletInfoAccess};
use #pezframe_support::__private::hashing::twox_128;
use #pezframe_support::storage::unhashed::contains_prefixed_key;
#pezframe_support::__private::pezsp_tracing::enter_span!(
#pezframe_support::__private::pezsp_tracing::trace_span!("before_all")
);
// Check if the pallet has any keys set, including the storage version. If there are
// no keys set, the pallet was just added to the runtime and needs to have its
// version initialized.
let pezpallet_hashed_prefix = <Self as PalletInfoAccess>::name_hash();
let exists = contains_prefixed_key(&pezpallet_hashed_prefix);
if !exists {
#initialize_on_chain_storage_version
<T as #pezframe_system::Config>::DbWeight::get().reads_writes(1, 1)
} else {
<T as #pezframe_system::Config>::DbWeight::get().reads(1)
}
}
}
impl<#type_impl_gen>
#pezframe_support::traits::OnRuntimeUpgrade
for #pezpallet_ident<#type_use_gen> #where_clause
{
fn on_runtime_upgrade() -> #pezframe_support::weights::Weight {
#pezframe_support::__private::pezsp_tracing::enter_span!(
#pezframe_support::__private::pezsp_tracing::trace_span!("on_runtime_update")
);
// log info about the upgrade.
#log_runtime_upgrade
<
Self as #pezframe_support::traits::Hooks<
#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>
>
>::on_runtime_upgrade()
}
#pezframe_support::try_runtime_enabled! {
fn pre_upgrade() -> Result<#pezframe_support::__private::Vec<u8>, #pezframe_support::pezsp_runtime::TryRuntimeError> {
<
Self
as
#pezframe_support::traits::Hooks<#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>>
>::pre_upgrade()
}
fn post_upgrade(state: #pezframe_support::__private::Vec<u8>) -> Result<(), #pezframe_support::pezsp_runtime::TryRuntimeError> {
#post_storage_version_check
<
Self
as
#pezframe_support::traits::Hooks<#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>>
>::post_upgrade(state)
}
}
}
impl<#type_impl_gen>
#pezframe_support::traits::OffchainWorker<#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>>
for #pezpallet_ident<#type_use_gen> #where_clause
{
fn offchain_worker(n: #pezframe_system::pezpallet_prelude::BlockNumberFor::<T>) {
<
Self as #pezframe_support::traits::Hooks<
#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>
>
>::offchain_worker(n)
}
}
// Integrity tests are only required for when `std` is enabled.
#pezframe_support::std_enabled! {
impl<#type_impl_gen>
#pezframe_support::traits::IntegrityTest
for #pezpallet_ident<#type_use_gen> #where_clause
{
fn integrity_test() {
#pezframe_support::__private::pezsp_io::TestExternalities::default().execute_with(|| {
<
Self as #pezframe_support::traits::Hooks<
#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>
>
>::integrity_test()
});
}
}
}
#pezframe_support::try_runtime_enabled! {
impl<#type_impl_gen>
#pezframe_support::traits::TryState<#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>>
for #pezpallet_ident<#type_use_gen> #where_clause
{
fn try_state(
n: #pezframe_system::pezpallet_prelude::BlockNumberFor::<T>,
_s: #pezframe_support::traits::TryStateSelect
) -> Result<(), #pezframe_support::pezsp_runtime::TryRuntimeError> {
#pezframe_support::__private::log::info!(
target: #pezframe_support::LOG_TARGET,
"🩺 Running {:?} try-state checks",
#pezpallet_name,
);
<
Self as #pezframe_support::traits::Hooks<
#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>
>
>::try_state(n).inspect_err(|err| {
#pezframe_support::__private::log::error!(
target: #pezframe_support::LOG_TARGET,
"❌ {:?} try_state checks failed: {:?}",
#pezpallet_name,
err
);
})
}
}
}
)
}
@@ -0,0 +1,55 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{pallet::Def, COUNTER};
use proc_macro2::TokenStream;
use quote::quote;
use syn::{spanned::Spanned, Ident};
pub fn expand_inherents(def: &mut Def) -> TokenStream {
let count = COUNTER.with(|counter| counter.borrow_mut().inc());
let macro_ident = Ident::new(&format!("__is_inherent_part_defined_{}", count), def.item.span());
let maybe_compile_error = if def.inherent.is_none() {
quote! {
compile_error!(concat!(
"`",
stringify!($pezpallet_name),
"` does not have #[pallet::inherent] defined, perhaps you should \
remove `Inherent` from construct_runtime?",
));
}
} else {
TokenStream::new()
};
quote! {
#[doc(hidden)]
pub mod __bizinikiwi_inherent_check {
#[macro_export]
#[doc(hidden)]
macro_rules! #macro_ident {
($pezpallet_name:ident) => {
#maybe_compile_error
}
}
#[doc(hidden)]
pub use #macro_ident as is_inherent_part_defined;
}
}
}
@@ -0,0 +1,43 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{pallet::Def, NUMBER_OF_INSTANCE};
use proc_macro2::Span;
///
/// * Provide inherent instance to be used by construct_runtime
/// * Provide Instance1 ..= Instance16 for instantiable pallet
pub fn expand_instances(def: &mut Def) -> proc_macro2::TokenStream {
let pezframe_support = &def.pezframe_support;
let inherent_ident = syn::Ident::new(crate::INHERENT_INSTANCE_NAME, Span::call_site());
let instances = if def.config.has_instance {
(1..=NUMBER_OF_INSTANCE)
.map(|i| syn::Ident::new(&format!("Instance{}", i), Span::call_site()))
.collect()
} else {
vec![]
};
quote::quote!(
/// Hidden instance generated to be internally used when module is used without
/// instance.
#[doc(hidden)]
pub type #inherent_ident = ();
#( pub use #pezframe_support::instances::#instances; )*
)
}
@@ -0,0 +1,136 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
mod call;
mod composite;
mod config;
mod constants;
mod doc_only;
mod documentation;
mod error;
mod event;
mod genesis_build;
mod genesis_config;
mod hooks;
mod inherent;
mod instances;
mod origin;
mod pezpallet_struct;
mod storage;
mod tasks;
mod tt_default_parts;
mod type_value;
mod validate_unsigned;
mod view_functions;
mod warnings;
use crate::pallet::Def;
use quote::ToTokens;
/// Merge where clause together, `where` token span is taken from the first not none one.
pub fn merge_where_clauses(clauses: &[&Option<syn::WhereClause>]) -> Option<syn::WhereClause> {
let mut clauses = clauses.iter().filter_map(|f| f.as_ref());
let mut res = clauses.next()?.clone();
for other in clauses {
res.predicates.extend(other.predicates.iter().cloned())
}
Some(res)
}
/// Expand definition, in particular:
/// * add some bounds and variants to type defined,
/// * create some new types,
/// * impl stuff on them.
pub fn expand(mut def: Def) -> proc_macro2::TokenStream {
// Remove the `pezpallet_doc` attribute first.
let metadata_docs = documentation::expand_documentation(&mut def);
let constants = constants::expand_constants(&mut def);
let pezpallet_struct = pezpallet_struct::expand_pallet_struct(&mut def);
let config = config::expand_config(&mut def);
let associated_types = config::expand_config_metadata(&def);
let call = call::expand_call(&mut def);
let tasks = tasks::expand_tasks(&mut def);
let error = error::expand_error(&mut def);
let event = event::expand_event(&mut def);
let storages = storage::expand_storages(&mut def);
let view_functions = view_functions::expand_view_functions(&def);
let inherents = inherent::expand_inherents(&mut def);
let instances = instances::expand_instances(&mut def);
let hooks = hooks::expand_hooks(&mut def);
let genesis_build = genesis_build::expand_genesis_build(&mut def);
let genesis_config = genesis_config::expand_genesis_config(&mut def);
let type_values = type_value::expand_type_values(&mut def);
let origin = origin::expand_origin(&mut def);
let validate_unsigned = validate_unsigned::expand_validate_unsigned(&mut def);
let tt_default_parts = tt_default_parts::expand_tt_default_parts(&mut def);
let doc_only = doc_only::expand_doc_only(&mut def);
let composites = composite::expand_composites(&mut def);
let warnings = def.config.warnings;
def.item.attrs.insert(
0,
syn::parse_quote!(
#[doc = r"The `pallet` module in each FRAME pallet hosts the most important items needed
to construct this pallet.
The main components of this pallet are:
- [`Pallet`], which implements all of the dispatchable extrinsics of the pallet, among
other public functions.
- The subset of the functions that are dispatchable can be identified either in the
[`dispatchables`] module or in the [`Call`] enum.
- [`storage_types`], which contains the list of all types that are representing a
storage item. Otherwise, all storage items are listed among [*Type Definitions*](#types).
- [`Config`], which contains the configuration trait of this pallet.
- [`Event`] and [`Error`], which are listed among the [*Enums*](#enums).
"]
),
);
let new_items = quote::quote!(
#(
#warnings
)*
#metadata_docs
#constants
#pezpallet_struct
#config
#associated_types
#call
#tasks
#error
#event
#storages
#view_functions
#inherents
#instances
#hooks
#genesis_build
#genesis_config
#type_values
#origin
#validate_unsigned
#tt_default_parts
#doc_only
#composites
);
let item = &mut def.item.content.as_mut().expect("This is checked by parsing").1;
item.push(syn::Item::Verbatim(new_items));
def.item.into_token_stream()
}
@@ -0,0 +1,56 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{pallet::Def, COUNTER};
use proc_macro2::TokenStream;
use quote::quote;
use syn::{spanned::Spanned, Ident};
/// expand the `is_origin_part_defined` macro.
pub fn expand_origin(def: &mut Def) -> TokenStream {
let count = COUNTER.with(|counter| counter.borrow_mut().inc());
let macro_ident = Ident::new(&format!("__is_origin_part_defined_{}", count), def.item.span());
let maybe_compile_error = if def.origin.is_none() {
quote! {
compile_error!(concat!(
"`",
stringify!($pezpallet_name),
"` does not have #[pallet::origin] defined, perhaps you should \
remove `Origin` from construct_runtime?",
));
}
} else {
TokenStream::new()
};
quote! {
#[doc(hidden)]
pub mod __bizinikiwi_origin_check {
#[macro_export]
#[doc(hidden)]
macro_rules! #macro_ident {
($pezpallet_name:ident) => {
#maybe_compile_error
}
}
#[doc(hidden)]
pub use #macro_ident as is_origin_part_defined;
}
}
}
@@ -0,0 +1,308 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::pallet::{expand::merge_where_clauses, Def};
use pezframe_support_procedural_tools::get_doc_literals;
///
/// * Add derive trait on Pallet
/// * Implement GetStorageVersion on Pallet
/// * Implement OnGenesis on Pallet
/// * Implement `fn error_metadata` on Pallet
/// * declare Module type alias for construct_runtime
/// * replace the first field type of `struct Pallet` with `PhantomData` if it is `_`
/// * implementation of `PalletInfoAccess` information
/// * implementation of `StorageInfoTrait` on Pallet
pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream {
let pezframe_support = &def.pezframe_support;
let pezframe_system = &def.pezframe_system;
let type_impl_gen = &def.type_impl_generics(def.pezpallet_struct.attr_span);
let type_use_gen = &def.type_use_generics(def.pezpallet_struct.attr_span);
let type_decl_gen = &def.type_decl_generics(def.pezpallet_struct.attr_span);
let pezpallet_ident = &def.pezpallet_struct.pallet;
let config_where_clause = &def.config.where_clause;
let deprecation_status =
match crate::deprecation::get_deprecation(&quote::quote! {#pezframe_support}, &def.item.attrs)
{
Ok(deprecation) => deprecation,
Err(e) => return e.into_compile_error(),
};
let mut storages_where_clauses = vec![&def.config.where_clause];
storages_where_clauses.extend(def.storages.iter().map(|storage| &storage.where_clause));
let storages_where_clauses = merge_where_clauses(&storages_where_clauses);
let pezpallet_item = {
let pezpallet_module_items = &mut def.item.content.as_mut().expect("Checked by def").1;
let item = &mut pezpallet_module_items[def.pezpallet_struct.index];
if let syn::Item::Struct(item) = item {
item
} else {
unreachable!("Checked by pallet struct parser")
}
};
// If the first field type is `_` then we replace with `PhantomData`
if let Some(field) = pezpallet_item.fields.iter_mut().next() {
if field.ty == syn::parse_quote!(_) {
field.ty = syn::parse_quote!(
core::marker::PhantomData<(#type_use_gen)>
);
}
}
if get_doc_literals(&pezpallet_item.attrs).is_empty() {
pezpallet_item.attrs.push(syn::parse_quote!(
#[doc = r"
The `Pallet` struct, the main type that implements traits and standalone
functions within the pallet.
"]
));
}
pezpallet_item.attrs.push(syn::parse_quote!(
#[derive(
#pezframe_support::CloneNoBound,
#pezframe_support::EqNoBound,
#pezframe_support::PartialEqNoBound,
#pezframe_support::RuntimeDebugNoBound,
)]
));
let pezpallet_error_metadata = if let Some(error_def) = &def.error {
let error_ident = &error_def.error;
quote::quote_spanned!(def.pezpallet_struct.attr_span =>
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen> #config_where_clause {
#[doc(hidden)]
#[allow(deprecated)]
pub fn error_metadata() -> Option<#pezframe_support::__private::metadata_ir::PalletErrorMetadataIR> {
Some(<#error_ident<#type_use_gen>>::error_metadata())
}
}
)
} else {
quote::quote_spanned!(def.pezpallet_struct.attr_span =>
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen> #config_where_clause {
#[doc(hidden)]
pub fn error_metadata() -> Option<#pezframe_support::__private::metadata_ir::PalletErrorMetadataIR> {
None
}
}
)
};
let storage_info_span =
def.pezpallet_struct.without_storage_info.unwrap_or(def.pezpallet_struct.attr_span);
let storage_names = &def.storages.iter().map(|storage| &storage.ident).collect::<Vec<_>>();
let storage_cfg_attrs =
&def.storages.iter().map(|storage| &storage.cfg_attrs).collect::<Vec<_>>();
let storage_maybe_allow_attrs = &def
.storages
.iter()
.map(|storage| crate::deprecation::extract_or_return_allow_attrs(&storage.attrs).collect())
.collect::<Vec<Vec<_>>>();
// Depending on the flag `without_storage_info` and the storage attribute `unbounded`, we use
// partial or full storage info from storage.
let storage_info_traits = &def
.storages
.iter()
.map(|storage| {
if storage.unbounded || def.pezpallet_struct.without_storage_info.is_some() {
quote::quote_spanned!(storage_info_span => PartialStorageInfoTrait)
} else {
quote::quote_spanned!(storage_info_span => StorageInfoTrait)
}
})
.collect::<Vec<_>>();
let storage_info_methods = &def
.storages
.iter()
.map(|storage| {
if storage.unbounded || def.pezpallet_struct.without_storage_info.is_some() {
quote::quote_spanned!(storage_info_span => partial_storage_info)
} else {
quote::quote_spanned!(storage_info_span => storage_info)
}
})
.collect::<Vec<_>>();
let storage_info = quote::quote_spanned!(storage_info_span =>
impl<#type_impl_gen> #pezframe_support::traits::StorageInfoTrait
for #pezpallet_ident<#type_use_gen>
#storages_where_clauses
{
fn storage_info()
-> #pezframe_support::__private::Vec<#pezframe_support::traits::StorageInfo>
{
#[allow(unused_mut)]
let mut res = #pezframe_support::__private::vec![];
#(
#(#storage_cfg_attrs)*
#(#storage_maybe_allow_attrs)*
{
let mut storage_info = <
#storage_names<#type_use_gen>
as #pezframe_support::traits::#storage_info_traits
>::#storage_info_methods();
res.append(&mut storage_info);
}
)*
res
}
}
);
let (storage_version, in_code_storage_version_ty) =
if let Some(v) = def.pezpallet_struct.storage_version.as_ref() {
(quote::quote! { #v }, quote::quote! { #pezframe_support::traits::StorageVersion })
} else {
(
quote::quote! { core::default::Default::default() },
quote::quote! { #pezframe_support::traits::NoStorageVersionSet },
)
};
let whitelisted_storage_idents: Vec<syn::Ident> = def
.storages
.iter()
.filter_map(|s| s.whitelisted.then(|| s.ident.clone()))
.collect();
let whitelisted_storage_keys_impl = quote::quote![
use #pezframe_support::traits::{StorageInfoTrait, TrackedStorageKey, WhitelistedStorageKeys};
impl<#type_impl_gen> WhitelistedStorageKeys for #pezpallet_ident<#type_use_gen> #storages_where_clauses {
fn whitelisted_storage_keys() -> #pezframe_support::__private::Vec<TrackedStorageKey> {
use #pezframe_support::__private::vec;
vec![#(
TrackedStorageKey::new(#whitelisted_storage_idents::<#type_use_gen>::hashed_key().to_vec())
),*]
}
}
];
quote::quote_spanned!(def.pezpallet_struct.attr_span =>
#pezpallet_error_metadata
/// Type alias to `Pallet`, to be used by `construct_runtime`.
///
/// Generated by `pallet` attribute macro.
#[deprecated(note = "use `Pallet` instead")]
#[allow(dead_code)]
pub type Module<#type_decl_gen> = #pezpallet_ident<#type_use_gen>;
// Implement `GetStorageVersion` for `Pallet`
impl<#type_impl_gen> #pezframe_support::traits::GetStorageVersion
for #pezpallet_ident<#type_use_gen>
#config_where_clause
{
type InCodeStorageVersion = #in_code_storage_version_ty;
fn in_code_storage_version() -> Self::InCodeStorageVersion {
#storage_version
}
fn on_chain_storage_version() -> #pezframe_support::traits::StorageVersion {
#pezframe_support::traits::StorageVersion::get::<Self>()
}
}
// Implement `OnGenesis` for `Pallet`
impl<#type_impl_gen> #pezframe_support::traits::OnGenesis
for #pezpallet_ident<#type_use_gen>
#config_where_clause
{
fn on_genesis() {
let storage_version: #pezframe_support::traits::StorageVersion = #storage_version;
storage_version.put::<Self>();
}
}
// Implement `PalletInfoAccess` for `Pallet`
impl<#type_impl_gen> #pezframe_support::traits::PalletInfoAccess
for #pezpallet_ident<#type_use_gen>
#config_where_clause
{
fn index() -> usize {
<
<T as #pezframe_system::Config>::PalletInfo as #pezframe_support::traits::PalletInfo
>::index::<Self>()
.expect("Pallet is part of the runtime because pallet `Config` trait is \
implemented by the runtime")
}
fn name() -> &'static str {
<
<T as #pezframe_system::Config>::PalletInfo as #pezframe_support::traits::PalletInfo
>::name::<Self>()
.expect("Pallet is part of the runtime because pallet `Config` trait is \
implemented by the runtime")
}
fn name_hash() -> [u8; 16] {
<
<T as #pezframe_system::Config>::PalletInfo as #pezframe_support::traits::PalletInfo
>::name_hash::<Self>()
.expect("Pallet is part of the runtime because pallet `Config` trait is \
implemented by the runtime")
}
fn module_name() -> &'static str {
<
<T as #pezframe_system::Config>::PalletInfo as #pezframe_support::traits::PalletInfo
>::module_name::<Self>()
.expect("Pallet is part of the runtime because pallet `Config` trait is \
implemented by the runtime")
}
fn crate_version() -> #pezframe_support::traits::CrateVersion {
#pezframe_support::crate_to_crate_version!()
}
}
impl<#type_impl_gen> #pezframe_support::traits::PalletsInfoAccess
for #pezpallet_ident<#type_use_gen>
#config_where_clause
{
fn count() -> usize { 1 }
fn infos() -> #pezframe_support::__private::Vec<#pezframe_support::traits::PalletInfoData> {
use #pezframe_support::traits::PalletInfoAccess;
let item = #pezframe_support::traits::PalletInfoData {
index: Self::index(),
name: Self::name(),
module_name: Self::module_name(),
crate_version: Self::crate_version(),
};
#pezframe_support::__private::vec![item]
}
}
#storage_info
#whitelisted_storage_keys_impl
impl<#type_use_gen> #pezpallet_ident<#type_use_gen> {
#[allow(dead_code)]
#[doc(hidden)]
pub fn deprecation_info() -> #pezframe_support::__private::metadata_ir::ItemDeprecationInfoIR {
#deprecation_status
}
}
)
}
@@ -0,0 +1,946 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
counter_prefix,
deprecation::extract_or_return_allow_attrs,
pallet::{
parse::{
helper::two128_str,
storage::{Metadata, QueryKind, StorageDef, StorageGenerics},
},
Def,
},
};
use quote::ToTokens;
use std::{collections::HashMap, ops::IndexMut};
use syn::spanned::Spanned;
/// Generate the prefix_ident related to the storage.
/// prefix_ident is used for the prefix struct to be given to storage as first generic param.
fn prefix_ident(storage: &StorageDef) -> syn::Ident {
let storage_ident = &storage.ident;
syn::Ident::new(&format!("_GeneratedPrefixForStorage{}", storage_ident), storage_ident.span())
}
/// Generate the counter_prefix_ident related to the storage.
/// counter_prefix_ident is used for the prefix struct to be given to counted storage map.
fn counter_prefix_ident(storage_ident: &syn::Ident) -> syn::Ident {
syn::Ident::new(
&format!("_GeneratedCounterPrefixForStorage{}", storage_ident),
storage_ident.span(),
)
}
/// Check for duplicated storage prefixes. This step is necessary since users can specify an
/// alternative storage prefix using the #[pallet::storage_prefix] syntax, and we need to ensure
/// that the prefix specified by the user is not a duplicate of an existing one.
fn check_prefix_duplicates(
storage_def: &StorageDef,
// A hashmap of all already used prefix and their associated error if duplication
used_prefixes: &mut HashMap<String, syn::Error>,
) -> syn::Result<()> {
let prefix = storage_def.prefix();
let dup_err = syn::Error::new(
storage_def.prefix_span(),
format!("Duplicate storage prefixes found for `{}`", prefix),
);
if let Some(other_dup_err) = used_prefixes.insert(prefix.clone(), dup_err.clone()) {
let mut err = dup_err;
err.combine(other_dup_err);
return Err(err);
}
if let Metadata::CountedMap { .. } = storage_def.metadata {
let counter_prefix = counter_prefix(&prefix);
let counter_dup_err = syn::Error::new(
storage_def.prefix_span(),
format!(
"Duplicate storage prefixes found for `{}`, used for counter associated to \
counted storage map",
counter_prefix,
),
);
if let Some(other_dup_err) = used_prefixes.insert(counter_prefix, counter_dup_err.clone()) {
let mut err = counter_dup_err;
err.combine(other_dup_err);
return Err(err);
}
}
Ok(())
}
pub struct ResultOnEmptyStructMetadata {
/// The Rust ident that is going to be used as the name of the OnEmpty struct.
pub name: syn::Ident,
/// The path to the error type being returned by the ResultQuery.
pub error_path: syn::Path,
/// The visibility of the OnEmpty struct.
pub visibility: syn::Visibility,
/// The type of the storage item.
pub value_ty: syn::Type,
/// The name of the pallet error enum variant that is going to be returned.
pub variant_name: syn::Ident,
/// The span used to report compilation errors about the OnEmpty struct.
pub span: proc_macro2::Span,
}
///
/// * if generics are unnamed: replace the first generic `_` by the generated prefix structure
/// * if generics are named: reorder the generic, remove their name, and add the missing ones.
/// * Add `#[allow(type_alias_bounds)]`
pub fn process_generics(def: &mut Def) -> syn::Result<Vec<ResultOnEmptyStructMetadata>> {
let pezframe_support = &def.pezframe_support;
let mut on_empty_struct_metadata = Vec::new();
for storage_def in def.storages.iter_mut() {
let item = &mut def.item.content.as_mut().expect("Checked by def").1[storage_def.index];
let typ_item = match item {
syn::Item::Type(t) => t,
_ => unreachable!("Checked by def"),
};
typ_item.attrs.push(syn::parse_quote!(#[allow(type_alias_bounds)]));
let typ_path = match &mut *typ_item.ty {
syn::Type::Path(p) => p,
_ => unreachable!("Checked by def"),
};
let args = match &mut typ_path.path.segments[0].arguments {
syn::PathArguments::AngleBracketed(args) => args,
_ => unreachable!("Checked by def"),
};
let prefix_ident = prefix_ident(storage_def);
let type_use_gen = if def.config.has_instance {
quote::quote_spanned!(storage_def.attr_span => T, I)
} else {
quote::quote_spanned!(storage_def.attr_span => T)
};
let default_query_kind: syn::Type =
syn::parse_quote!(#pezframe_support::storage::types::OptionQuery);
let mut default_on_empty = |value_ty: syn::Type| -> syn::Type {
if let Some(QueryKind::ResultQuery(error_path, variant_name)) =
storage_def.query_kind.as_ref()
{
let on_empty_ident =
quote::format_ident!("__Frame_Internal_Get{}Result", storage_def.ident);
on_empty_struct_metadata.push(ResultOnEmptyStructMetadata {
name: on_empty_ident.clone(),
visibility: storage_def.vis.clone(),
value_ty,
error_path: error_path.clone(),
variant_name: variant_name.clone(),
span: storage_def.attr_span,
});
return syn::parse_quote!(#on_empty_ident);
}
syn::parse_quote!(#pezframe_support::traits::GetDefault)
};
let default_max_values: syn::Type = syn::parse_quote!(#pezframe_support::traits::GetDefault);
let set_result_query_type_parameter = |query_type: &mut syn::Type| -> syn::Result<()> {
if let Some(QueryKind::ResultQuery(error_path, _)) = storage_def.query_kind.as_ref() {
if let syn::Type::Path(syn::TypePath { path: syn::Path { segments, .. }, .. }) =
query_type
{
if let Some(seg) = segments.last_mut() {
if let syn::PathArguments::AngleBracketed(
syn::AngleBracketedGenericArguments { args, .. },
) = &mut seg.arguments
{
args.clear();
args.push(syn::GenericArgument::Type(syn::parse_quote!(#error_path)));
}
}
} else {
let msg = format!(
"Invalid pallet::storage, unexpected type for query, expected ResultQuery \
with 1 type parameter, found `{}`",
query_type.to_token_stream().to_string()
);
return Err(syn::Error::new(query_type.span(), msg));
}
}
Ok(())
};
if let Some(named_generics) = storage_def.named_generics.clone() {
args.args.clear();
args.args.push(syn::parse_quote!( #prefix_ident<#type_use_gen> ));
match named_generics {
StorageGenerics::Value { value, query_kind, on_empty } => {
args.args.push(syn::GenericArgument::Type(value.clone()));
let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone());
set_result_query_type_parameter(&mut query_kind)?;
args.args.push(syn::GenericArgument::Type(query_kind));
let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value));
args.args.push(syn::GenericArgument::Type(on_empty));
},
StorageGenerics::Map { hasher, key, value, query_kind, on_empty, max_values } |
StorageGenerics::CountedMap {
hasher,
key,
value,
query_kind,
on_empty,
max_values,
} => {
args.args.push(syn::GenericArgument::Type(hasher));
args.args.push(syn::GenericArgument::Type(key));
args.args.push(syn::GenericArgument::Type(value.clone()));
let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone());
set_result_query_type_parameter(&mut query_kind)?;
args.args.push(syn::GenericArgument::Type(query_kind));
let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value));
args.args.push(syn::GenericArgument::Type(on_empty));
let max_values = max_values.unwrap_or_else(|| default_max_values.clone());
args.args.push(syn::GenericArgument::Type(max_values));
},
StorageGenerics::DoubleMap {
hasher1,
key1,
hasher2,
key2,
value,
query_kind,
on_empty,
max_values,
} => {
args.args.push(syn::GenericArgument::Type(hasher1));
args.args.push(syn::GenericArgument::Type(key1));
args.args.push(syn::GenericArgument::Type(hasher2));
args.args.push(syn::GenericArgument::Type(key2));
args.args.push(syn::GenericArgument::Type(value.clone()));
let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone());
set_result_query_type_parameter(&mut query_kind)?;
args.args.push(syn::GenericArgument::Type(query_kind));
let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value));
args.args.push(syn::GenericArgument::Type(on_empty));
let max_values = max_values.unwrap_or_else(|| default_max_values.clone());
args.args.push(syn::GenericArgument::Type(max_values));
},
StorageGenerics::NMap { keygen, value, query_kind, on_empty, max_values } |
StorageGenerics::CountedNMap {
keygen,
value,
query_kind,
on_empty,
max_values,
} => {
args.args.push(syn::GenericArgument::Type(keygen));
args.args.push(syn::GenericArgument::Type(value.clone()));
let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone());
set_result_query_type_parameter(&mut query_kind)?;
args.args.push(syn::GenericArgument::Type(query_kind));
let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value));
args.args.push(syn::GenericArgument::Type(on_empty));
let max_values = max_values.unwrap_or_else(|| default_max_values.clone());
args.args.push(syn::GenericArgument::Type(max_values));
},
}
} else {
args.args[0] = syn::parse_quote!( #prefix_ident<#type_use_gen> );
let (value_idx, query_idx, on_empty_idx) = match storage_def.metadata {
Metadata::Value { .. } => (1, 2, 3),
Metadata::NMap { .. } | Metadata::CountedNMap { .. } => (2, 3, 4),
Metadata::Map { .. } | Metadata::CountedMap { .. } => (3, 4, 5),
Metadata::DoubleMap { .. } => (5, 6, 7),
};
if storage_def.use_default_hasher {
let hasher_indices: Vec<usize> = match storage_def.metadata {
Metadata::Map { .. } | Metadata::CountedMap { .. } => vec![1],
Metadata::DoubleMap { .. } => vec![1, 3],
_ => vec![],
};
for hasher_idx in hasher_indices {
args.args[hasher_idx] = syn::GenericArgument::Type(
syn::parse_quote!(#pezframe_support::Blake2_128Concat),
);
}
}
if query_idx < args.args.len() {
if let syn::GenericArgument::Type(query_kind) = args.args.index_mut(query_idx) {
set_result_query_type_parameter(query_kind)?;
}
} else if let Some(QueryKind::ResultQuery(error_path, _)) =
storage_def.query_kind.as_ref()
{
args.args.push(syn::GenericArgument::Type(syn::parse_quote!(#error_path)))
}
// Here, we only need to check if OnEmpty is *not* specified, and if so, then we have to
// generate a default OnEmpty struct for it.
if on_empty_idx >= args.args.len() &&
matches!(storage_def.query_kind.as_ref(), Some(QueryKind::ResultQuery(_, _)))
{
let value_ty = match args.args[value_idx].clone() {
syn::GenericArgument::Type(ty) => ty,
_ => unreachable!(),
};
let on_empty = default_on_empty(value_ty);
args.args.push(syn::GenericArgument::Type(on_empty));
}
}
}
Ok(on_empty_struct_metadata)
}
fn augment_final_docs(def: &mut Def) {
// expand the docs with a new line showing the storage type (value, map, double map, etc), and
// the key/value type(s).
let mut push_string_literal = |doc_line: &str, storage: &mut StorageDef| {
let item = &mut def.item.content.as_mut().expect("Checked by def").1[storage.index];
let typ_item = match item {
syn::Item::Type(t) => t,
_ => unreachable!("Checked by def"),
};
typ_item.attrs.push(syn::parse_quote!(#[doc = ""]));
typ_item.attrs.push(syn::parse_quote!(#[doc = #doc_line]));
};
def.storages.iter_mut().for_each(|storage| match &storage.metadata {
Metadata::Value { value } => {
let doc_line = format!(
"Storage type is [`StorageValue`] with value type `{}`.",
value.to_token_stream()
);
push_string_literal(&doc_line, storage);
},
Metadata::Map { key, value } => {
let doc_line = format!(
"Storage type is [`StorageMap`] with key type `{}` and value type `{}`.",
key.to_token_stream(),
value.to_token_stream()
);
push_string_literal(&doc_line, storage);
},
Metadata::DoubleMap { key1, key2, value } => {
let doc_line = format!(
"Storage type is [`StorageDoubleMap`] with key1 type {}, key2 type {} and value type {}.",
key1.to_token_stream(),
key2.to_token_stream(),
value.to_token_stream()
);
push_string_literal(&doc_line, storage);
},
Metadata::NMap { keys, value, .. } => {
let doc_line = format!(
"Storage type is [`StorageNMap`] with keys type ({}) and value type {}.",
keys.iter()
.map(|k| k.to_token_stream().to_string())
.collect::<Vec<_>>()
.join(", "),
value.to_token_stream()
);
push_string_literal(&doc_line, storage);
},
Metadata::CountedNMap { keys, value, .. } => {
let doc_line = format!(
"Storage type is [`CountedStorageNMap`] with keys type ({}) and value type {}.",
keys.iter()
.map(|k| k.to_token_stream().to_string())
.collect::<Vec<_>>()
.join(", "),
value.to_token_stream()
);
push_string_literal(&doc_line, storage);
},
Metadata::CountedMap { key, value } => {
let doc_line = format!(
"Storage type is [`CountedStorageMap`] with key type {} and value type {}.",
key.to_token_stream(),
value.to_token_stream()
);
push_string_literal(&doc_line, storage);
},
});
}
///
/// * generate StoragePrefix structs (e.g. for a storage `MyStorage` a struct with the name
/// `_GeneratedPrefixForStorage$NameOfStorage` is generated) and implements StorageInstance trait.
/// * if generics are unnamed: replace the first generic `_` by the generated prefix structure
/// * if generics are named: reorder the generic, remove their name, and add the missing ones.
/// * Add `#[allow(type_alias_bounds)]` on storages type alias
/// * generate metadatas
pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
let on_empty_struct_metadata = match process_generics(def) {
Ok(idents) => idents,
Err(e) => return e.into_compile_error(),
};
augment_final_docs(def);
// Check for duplicate prefixes
let mut prefix_set = HashMap::new();
let mut errors = def
.storages
.iter()
.filter_map(|storage_def| check_prefix_duplicates(storage_def, &mut prefix_set).err());
if let Some(mut final_error) = errors.next() {
errors.for_each(|error| final_error.combine(error));
return final_error.into_compile_error();
}
let pezframe_support = &def.pezframe_support;
let pezframe_system = &def.pezframe_system;
let pezpallet_ident = &def.pezpallet_struct.pallet;
let mut entries_builder = vec![];
for storage in def.storages.iter() {
let no_docs = vec![];
let docs = if cfg!(feature = "no-metadata-docs") { &no_docs } else { &storage.docs };
let ident = &storage.ident;
let gen = &def.type_use_generics(storage.attr_span);
let full_ident = quote::quote_spanned!(storage.attr_span => #ident<#gen> );
let cfg_attrs = &storage.cfg_attrs;
let deprecation = match crate::deprecation::get_deprecation(
&quote::quote! { #pezframe_support },
&storage.attrs,
) {
Ok(deprecation) => deprecation,
Err(e) => return e.into_compile_error(),
};
// Extracts #[allow] attributes, necessary so that we don't run into compiler warnings
let maybe_allow_attrs: Vec<syn::Attribute> =
extract_or_return_allow_attrs(&storage.attrs).collect();
entries_builder.push(quote::quote_spanned!(storage.attr_span =>
#(#cfg_attrs)*
#(#maybe_allow_attrs)*
(|entries: &mut #pezframe_support::__private::Vec<_>| {
{
<#full_ident as #pezframe_support::storage::StorageEntryMetadataBuilder>::build_metadata(
#deprecation,
#pezframe_support::__private::vec![
#( #docs, )*
],
entries,
);
}
})
))
}
let getters = def.storages.iter().map(|storage| {
if let Some(getter) = &storage.getter {
let completed_where_clause =
super::merge_where_clauses(&[&storage.where_clause, &def.config.where_clause]);
// Extracts #[allow] attributes, necessary so that we don't run into compiler warnings
let maybe_allow_attrs: Vec<syn::Attribute> =
extract_or_return_allow_attrs(&storage.attrs).collect();
let ident = &storage.ident;
let gen = &def.type_use_generics(storage.attr_span);
let type_impl_gen = &def.type_impl_generics(storage.attr_span);
let type_use_gen = &def.type_use_generics(storage.attr_span);
let full_ident = quote::quote_spanned!(storage.attr_span => #ident<#gen> );
let cfg_attrs = &storage.cfg_attrs;
// If the storage item is public, link it and otherwise just mention it.
//
// We can not just copy the docs from a non-public type as it may links to internal
// types which makes the compiler very unhappy :(
let getter_doc_line = if matches!(storage.vis, syn::Visibility::Public(_)) {
format!("An auto-generated getter for [`{}`].", storage.ident)
} else {
format!("An auto-generated getter for `{}`.", storage.ident)
};
match &storage.metadata {
Metadata::Value { value } => {
let query = match storage.query_kind.as_ref().expect("Checked by def") {
QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span =>
Option<#value>
),
QueryKind::ResultQuery(error_path, _) => {
quote::quote_spanned!(storage.attr_span =>
Result<#value, #error_path>
)
},
QueryKind::ValueQuery => quote::quote!(#value),
};
quote::quote_spanned!(storage.attr_span =>
#(#cfg_attrs)*
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen> #completed_where_clause {
#[doc = #getter_doc_line]
#(#maybe_allow_attrs)*
pub fn #getter() -> #query {
<
#full_ident as #pezframe_support::storage::StorageValue<#value>
>::get()
}
}
)
},
Metadata::Map { key, value } => {
let query = match storage.query_kind.as_ref().expect("Checked by def") {
QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span =>
Option<#value>
),
QueryKind::ResultQuery(error_path, _) => {
quote::quote_spanned!(storage.attr_span =>
Result<#value, #error_path>
)
},
QueryKind::ValueQuery => quote::quote!(#value),
};
quote::quote_spanned!(storage.attr_span =>
#(#cfg_attrs)*
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen> #completed_where_clause {
#[doc = #getter_doc_line]
#(#maybe_allow_attrs)*
pub fn #getter<KArg>(k: KArg) -> #query where
KArg: #pezframe_support::__private::codec::EncodeLike<#key>,
{
<
#full_ident as #pezframe_support::storage::StorageMap<#key, #value>
>::get(k)
}
}
)
},
Metadata::CountedMap { key, value } => {
let query = match storage.query_kind.as_ref().expect("Checked by def") {
QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span =>
Option<#value>
),
QueryKind::ResultQuery(error_path, _) => {
quote::quote_spanned!(storage.attr_span =>
Result<#value, #error_path>
)
},
QueryKind::ValueQuery => quote::quote!(#value),
};
quote::quote_spanned!(storage.attr_span =>
#(#cfg_attrs)*
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen> #completed_where_clause {
#[doc = #getter_doc_line]
#(#maybe_allow_attrs)*
pub fn #getter<KArg>(k: KArg) -> #query where
KArg: #pezframe_support::__private::codec::EncodeLike<#key>,
{
// NOTE: we can't use any trait here because CountedStorageMap
// doesn't implement any.
<#full_ident>::get(k)
}
}
)
},
Metadata::DoubleMap { key1, key2, value } => {
let query = match storage.query_kind.as_ref().expect("Checked by def") {
QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span =>
Option<#value>
),
QueryKind::ResultQuery(error_path, _) => {
quote::quote_spanned!(storage.attr_span =>
Result<#value, #error_path>
)
},
QueryKind::ValueQuery => quote::quote!(#value),
};
quote::quote_spanned!(storage.attr_span =>
#(#cfg_attrs)*
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen> #completed_where_clause {
#[doc = #getter_doc_line]
#(#maybe_allow_attrs)*
pub fn #getter<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> #query where
KArg1: #pezframe_support::__private::codec::EncodeLike<#key1>,
KArg2: #pezframe_support::__private::codec::EncodeLike<#key2>,
{
<
#full_ident as
#pezframe_support::storage::StorageDoubleMap<#key1, #key2, #value>
>::get(k1, k2)
}
}
)
},
Metadata::NMap { keygen, value, .. } => {
let query = match storage.query_kind.as_ref().expect("Checked by def") {
QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span =>
Option<#value>
),
QueryKind::ResultQuery(error_path, _) => {
quote::quote_spanned!(storage.attr_span =>
Result<#value, #error_path>
)
},
QueryKind::ValueQuery => quote::quote!(#value),
};
quote::quote_spanned!(storage.attr_span =>
#(#cfg_attrs)*
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen> #completed_where_clause {
#[doc = #getter_doc_line]
#(#maybe_allow_attrs)*
pub fn #getter<KArg>(key: KArg) -> #query
where
KArg: #pezframe_support::storage::types::EncodeLikeTuple<
<#keygen as #pezframe_support::storage::types::KeyGenerator>::KArg
>
+ #pezframe_support::storage::types::TupleToEncodedIter,
{
<
#full_ident as
#pezframe_support::storage::StorageNMap<#keygen, #value>
>::get(key)
}
}
)
},
Metadata::CountedNMap { keygen, value, .. } => {
let query = match storage.query_kind.as_ref().expect("Checked by def") {
QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span =>
Option<#value>
),
QueryKind::ResultQuery(error_path, _) => {
quote::quote_spanned!(storage.attr_span =>
Result<#value, #error_path>
)
},
QueryKind::ValueQuery => quote::quote!(#value),
};
quote::quote_spanned!(storage.attr_span =>
#(#cfg_attrs)*
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen> #completed_where_clause {
#[doc = #getter_doc_line]
#(#maybe_allow_attrs)*
pub fn #getter<KArg>(key: KArg) -> #query
where
KArg: #pezframe_support::storage::types::EncodeLikeTuple<
<#keygen as #pezframe_support::storage::types::KeyGenerator>::KArg
>
+ #pezframe_support::storage::types::TupleToEncodedIter,
{
// NOTE: we can't use any trait here because CountedStorageNMap
// doesn't implement any.
<#full_ident>::get(key)
}
}
)
},
}
} else {
Default::default()
}
});
let prefix_structs = def.storages.iter().map(|storage_def| {
let type_impl_gen = &def.type_impl_generics(storage_def.attr_span);
let type_use_gen = &def.type_use_generics(storage_def.attr_span);
let prefix_struct_ident = prefix_ident(storage_def);
let prefix_struct_vis = &storage_def.vis;
let prefix_struct_const = storage_def.prefix();
let config_where_clause = &def.config.where_clause;
let cfg_attrs = &storage_def.cfg_attrs;
let maybe_counter = match storage_def.metadata {
Metadata::CountedMap { .. } => {
let counter_prefix_struct_ident = counter_prefix_ident(&storage_def.ident);
let counter_prefix_struct_const = counter_prefix(&prefix_struct_const);
let storage_prefix_hash = two128_str(&counter_prefix_struct_const);
quote::quote_spanned!(storage_def.attr_span =>
#(#cfg_attrs)*
#[doc(hidden)]
#prefix_struct_vis struct #counter_prefix_struct_ident<#type_use_gen>(
core::marker::PhantomData<(#type_use_gen,)>
);
#(#cfg_attrs)*
impl<#type_impl_gen> #pezframe_support::traits::StorageInstance
for #counter_prefix_struct_ident<#type_use_gen>
#config_where_clause
{
fn pezpallet_prefix() -> &'static str {
<
<T as #pezframe_system::Config>::PalletInfo
as #pezframe_support::traits::PalletInfo
>::name::<Pallet<#type_use_gen>>()
.expect("No name found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`.")
}
fn pezpallet_prefix_hash() -> [u8; 16] {
<
<T as #pezframe_system::Config>::PalletInfo
as #pezframe_support::traits::PalletInfo
>::name_hash::<Pallet<#type_use_gen>>()
.expect("No name_hash found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`.")
}
const STORAGE_PREFIX: &'static str = #counter_prefix_struct_const;
fn storage_prefix_hash() -> [u8; 16] {
#storage_prefix_hash
}
}
#(#cfg_attrs)*
impl<#type_impl_gen> #pezframe_support::storage::types::CountedStorageMapInstance
for #prefix_struct_ident<#type_use_gen>
#config_where_clause
{
type CounterPrefix = #counter_prefix_struct_ident<#type_use_gen>;
}
)
},
Metadata::CountedNMap { .. } => {
let counter_prefix_struct_ident = counter_prefix_ident(&storage_def.ident);
let counter_prefix_struct_const = counter_prefix(&prefix_struct_const);
let storage_prefix_hash = two128_str(&counter_prefix_struct_const);
quote::quote_spanned!(storage_def.attr_span =>
#(#cfg_attrs)*
#[doc(hidden)]
#prefix_struct_vis struct #counter_prefix_struct_ident<#type_use_gen>(
core::marker::PhantomData<(#type_use_gen,)>
);
#(#cfg_attrs)*
impl<#type_impl_gen> #pezframe_support::traits::StorageInstance
for #counter_prefix_struct_ident<#type_use_gen>
#config_where_clause
{
fn pezpallet_prefix() -> &'static str {
<
<T as #pezframe_system::Config>::PalletInfo
as #pezframe_support::traits::PalletInfo
>::name::<Pallet<#type_use_gen>>()
.expect("No name found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`.")
}
fn pezpallet_prefix_hash() -> [u8; 16] {
<
<T as #pezframe_system::Config>::PalletInfo
as #pezframe_support::traits::PalletInfo
>::name_hash::<Pallet<#type_use_gen>>()
.expect("No name_hash found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`.")
}
const STORAGE_PREFIX: &'static str = #counter_prefix_struct_const;
fn storage_prefix_hash() -> [u8; 16] {
#storage_prefix_hash
}
}
#(#cfg_attrs)*
impl<#type_impl_gen> #pezframe_support::storage::types::CountedStorageNMapInstance
for #prefix_struct_ident<#type_use_gen>
#config_where_clause
{
type CounterPrefix = #counter_prefix_struct_ident<#type_use_gen>;
}
)
},
_ => proc_macro2::TokenStream::default(),
};
let storage_prefix_hash = two128_str(&prefix_struct_const);
quote::quote_spanned!(storage_def.attr_span =>
#maybe_counter
#(#cfg_attrs)*
#[doc(hidden)]
#prefix_struct_vis struct #prefix_struct_ident<#type_use_gen>(
core::marker::PhantomData<(#type_use_gen,)>
);
#(#cfg_attrs)*
impl<#type_impl_gen> #pezframe_support::traits::StorageInstance
for #prefix_struct_ident<#type_use_gen>
#config_where_clause
{
fn pezpallet_prefix() -> &'static str {
<
<T as #pezframe_system::Config>::PalletInfo
as #pezframe_support::traits::PalletInfo
>::name::<Pallet<#type_use_gen>>()
.expect("No name found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`.")
}
fn pezpallet_prefix_hash() -> [u8; 16] {
<
<T as #pezframe_system::Config>::PalletInfo
as #pezframe_support::traits::PalletInfo
>::name_hash::<Pallet<#type_use_gen>>()
.expect("No name_hash found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`.")
}
const STORAGE_PREFIX: &'static str = #prefix_struct_const;
fn storage_prefix_hash() -> [u8; 16] {
#storage_prefix_hash
}
}
)
});
let on_empty_structs = on_empty_struct_metadata.into_iter().map(|metadata| {
use crate::pallet::parse::GenericKind;
use syn::{GenericArgument, Path, PathArguments, PathSegment, Type, TypePath};
let ResultOnEmptyStructMetadata {
name,
visibility,
value_ty,
error_path,
variant_name,
span,
} = metadata;
let generic_kind = match error_path.segments.last() {
Some(PathSegment { arguments: PathArguments::AngleBracketed(args), .. }) => {
let (has_config, has_instance) =
args.args.iter().fold((false, false), |(has_config, has_instance), arg| {
match arg {
GenericArgument::Type(Type::Path(TypePath {
path: Path { segments, .. },
..
})) => {
let maybe_config =
segments.first().map_or(false, |seg| seg.ident == "T");
let maybe_instance =
segments.first().map_or(false, |seg| seg.ident == "I");
(has_config || maybe_config, has_instance || maybe_instance)
},
_ => (has_config, has_instance),
}
});
GenericKind::from_gens(has_config, has_instance).unwrap_or(GenericKind::None)
},
_ => GenericKind::None,
};
let type_impl_gen = generic_kind.type_impl_gen(proc_macro2::Span::call_site());
let config_where_clause = &def.config.where_clause;
quote::quote_spanned!(span =>
#[doc(hidden)]
#[allow(non_camel_case_types)]
#visibility struct #name;
impl<#type_impl_gen> #pezframe_support::traits::Get<Result<#value_ty, #error_path>>
for #name
#config_where_clause
{
#[allow(deprecated)]
fn get() -> Result<#value_ty, #error_path> {
Err(<#error_path>::#variant_name)
}
}
)
});
// aggregated where clause of all storage types and the whole pallet.
let mut where_clauses = vec![&def.config.where_clause];
where_clauses.extend(def.storages.iter().map(|storage| &storage.where_clause));
let completed_where_clause = super::merge_where_clauses(&where_clauses);
let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site());
let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site());
let try_decode_entire_state = {
let mut storage_names = def
.storages
.iter()
.filter_map(|storage| {
// A little hacky; don't generate for cfg gated storages to not get compile errors
// when building "frame-feature-testing" gated storages in the "pezframe-support-test"
// crate.
if storage.try_decode && storage.cfg_attrs.is_empty() {
let ident = &storage.ident;
let gen = &def.type_use_generics(storage.attr_span);
Some(quote::quote_spanned!(storage.attr_span => #ident<#gen> ))
} else {
None
}
})
.collect::<Vec<_>>();
storage_names.sort_by_cached_key(|ident| ident.to_string());
quote::quote!(
#pezframe_support::try_runtime_enabled! {
#[allow(deprecated)]
impl<#type_impl_gen> #pezframe_support::traits::TryDecodeEntireStorage
for #pezpallet_ident<#type_use_gen> #completed_where_clause
{
fn try_decode_entire_state() -> Result<usize, #pezframe_support::__private::Vec<#pezframe_support::traits::TryDecodeEntireStorageError>> {
let pezpallet_name = <<T as #pezframe_system::Config>::PalletInfo as #pezframe_support::traits::PalletInfo>
::name::<#pezpallet_ident<#type_use_gen>>()
.expect("Every active pallet has a name in the runtime; qed");
#pezframe_support::__private::log::debug!(target: "runtime::try-decode-state", "trying to decode pallet: {pezpallet_name}");
// NOTE: for now, we have to exclude storage items that are feature gated.
let mut errors = #pezframe_support::__private::Vec::new();
let mut decoded = 0usize;
#(
#pezframe_support::__private::log::debug!(target: "runtime::try-decode-state", "trying to decode storage: \
{pezpallet_name}::{}", stringify!(#storage_names));
match <#storage_names as #pezframe_support::traits::TryDecodeEntireStorage>::try_decode_entire_state() {
Ok(count) => {
decoded += count;
},
Err(err) => {
errors.extend(err);
},
}
)*
if errors.is_empty() {
Ok(decoded)
} else {
Err(errors)
}
}
}
}
)
};
quote::quote!(
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen>
#completed_where_clause
{
#[doc(hidden)]
pub fn storage_metadata() -> #pezframe_support::__private::metadata_ir::PalletStorageMetadataIR {
#pezframe_support::__private::metadata_ir::PalletStorageMetadataIR {
prefix: <
<T as #pezframe_system::Config>::PalletInfo as
#pezframe_support::traits::PalletInfo
>::name::<#pezpallet_ident<#type_use_gen>>()
.expect("No name found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`."),
entries: {
#[allow(unused_mut)]
let mut entries = #pezframe_support::__private::vec![];
#( #entries_builder(&mut entries); )*
entries
},
}
}
}
#( #getters )*
#( #prefix_structs )*
#( #on_empty_structs )*
#try_decode_entire_state
)
}
@@ -0,0 +1,232 @@
//! Contains logic for expanding task-related items.
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Home of the expansion code for the Tasks API
use crate::pallet::{parse::tasks::*, Def};
use inflector::Inflector;
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote, ToTokens};
use syn::{parse_quote_spanned, spanned::Spanned};
impl TaskEnumDef {
/// Since we optionally allow users to manually specify a `#[pallet::task_enum]`, in the
/// event they _don't_ specify one (which is actually the most common behavior) we have to
/// generate one based on the existing [`TasksDef`]. This method performs that generation.
pub fn generate(tasks: &TasksDef, def: &Def) -> Self {
// We use the span of the attribute to indicate that the error comes from code generated
// for the specific section, otherwise the item impl.
let span = tasks
.tasks_attr
.as_ref()
.map_or_else(|| tasks.item_impl.span(), |attr| attr.span());
let type_decl_bounded_generics = def.type_decl_bounded_generics(span);
let variants = if tasks.tasks_attr.is_some() {
tasks
.tasks
.iter()
.map(|task| {
let ident = &task.item.sig.ident;
let ident =
format_ident!("{}", ident.to_string().to_class_case(), span = ident.span());
let args = task.item.sig.inputs.iter().collect::<Vec<_>>();
if args.is_empty() {
quote!(#ident)
} else {
quote!(#ident {
#(#args),*
})
}
})
.collect::<Vec<_>>()
} else {
Vec::new()
};
parse_quote_spanned! { span =>
/// Auto-generated enum that encapsulates all tasks defined by this pallet.
///
/// Conceptually similar to the [`Call`] enum, but for tasks. This is only
/// generated if there are tasks present in this pallet.
#[pallet::task_enum]
pub enum Task<#type_decl_bounded_generics> {
#(
#variants,
)*
}
}
}
}
impl TaskEnumDef {
fn expand_to_tokens(&self, def: &Def) -> TokenStream2 {
if let Some(attr) = &self.attr {
let ident = &self.item_enum.ident;
let vis = &self.item_enum.vis;
let attrs = &self.item_enum.attrs;
let generics = &self.item_enum.generics;
let variants = &self.item_enum.variants;
let pezframe_support = &def.pezframe_support;
let type_use_generics = &def.type_use_generics(attr.span());
let type_impl_generics = &def.type_impl_generics(attr.span());
// `item_enum` is short-hand / generated enum
quote! {
#(#attrs)*
#[derive(
#pezframe_support::CloneNoBound,
#pezframe_support::EqNoBound,
#pezframe_support::PartialEqNoBound,
#pezframe_support::pezpallet_prelude::Encode,
#pezframe_support::pezpallet_prelude::Decode,
#pezframe_support::pezpallet_prelude::DecodeWithMemTracking,
#pezframe_support::pezpallet_prelude::TypeInfo,
)]
#[codec(encode_bound())]
#[codec(decode_bound())]
#[scale_info(skip_type_params(#type_use_generics))]
#vis enum #ident #generics {
#variants
#[doc(hidden)]
#[codec(skip)]
__Ignore(core::marker::PhantomData<(#type_use_generics)>, #pezframe_support::Never),
}
impl<#type_impl_generics> core::fmt::Debug for #ident<#type_use_generics> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct(stringify!(#ident)).field("value", self).finish()
}
}
}
} else {
// `item_enum` is a manually specified enum (no attribute)
self.item_enum.to_token_stream()
}
}
}
impl TasksDef {
fn expand_to_tokens(&self, def: &Def) -> TokenStream2 {
let pezframe_support = &def.pezframe_support;
let enum_ident = syn::Ident::new("Task", self.enum_ident.span());
let enum_arguments = &self.enum_arguments;
let enum_use = quote!(#enum_ident #enum_arguments);
let task_fn_idents = self
.tasks
.iter()
.map(|task| {
format_ident!(
"{}",
&task.item.sig.ident.to_string().to_class_case(),
span = task.item.sig.ident.span()
)
})
.collect::<Vec<_>>();
let task_indices = self.tasks.iter().map(|task| &task.index_attr.meta.index);
let task_conditions = self.tasks.iter().map(|task| &task.condition_attr.meta.expr);
let task_weights = self.tasks.iter().map(|task| &task.weight_attr.meta.expr);
let task_iters = self.tasks.iter().map(|task| &task.list_attr.meta.expr);
let task_fn_impls = self.tasks.iter().map(|task| {
let mut task_fn_impl = task.item.clone();
task_fn_impl.attrs = vec![];
task_fn_impl
});
let task_fn_names = self.tasks.iter().map(|task| &task.item.sig.ident);
let task_arg_names = self.tasks.iter().map(|task| &task.arg_names).collect::<Vec<_>>();
let impl_generics = &self.item_impl.generics;
quote! {
impl #impl_generics #enum_use
{
#(#task_fn_impls)*
}
impl #impl_generics #pezframe_support::traits::Task for #enum_use
{
type Enumeration = #pezframe_support::__private::IntoIter<#enum_use>;
fn iter() -> Self::Enumeration {
let mut all_tasks = #pezframe_support::__private::vec![];
#(all_tasks
.extend(#task_iters.map(|(#(#task_arg_names),*)| #enum_ident::#task_fn_idents { #(#task_arg_names: #task_arg_names.clone()),* })
.collect::<#pezframe_support::__private::Vec<_>>());
)*
all_tasks.into_iter()
}
fn task_index(&self) -> u32 {
match self.clone() {
#(#enum_ident::#task_fn_idents { .. } => #task_indices,)*
Task::__Ignore(_, _) => unreachable!(),
}
}
fn is_valid(&self) -> bool {
match self.clone() {
#(#enum_ident::#task_fn_idents { #(#task_arg_names),* } => (#task_conditions)(#(#task_arg_names),* ),)*
Task::__Ignore(_, _) => unreachable!(),
}
}
fn run(&self) -> Result<(), #pezframe_support::pezpallet_prelude::DispatchError> {
match self.clone() {
#(#enum_ident::#task_fn_idents { #(#task_arg_names),* } => {
<#enum_use>::#task_fn_names(#( #task_arg_names, )* )
},)*
Task::__Ignore(_, _) => unreachable!(),
}
}
#[allow(unused_variables)]
fn weight(&self) -> #pezframe_support::pezpallet_prelude::Weight {
match self.clone() {
#(#enum_ident::#task_fn_idents { #(#task_arg_names),* } => #task_weights,)*
Task::__Ignore(_, _) => unreachable!(),
}
}
}
}
}
}
/// Generate code related to tasks.
pub fn expand_tasks(def: &Def) -> TokenStream2 {
let Some(tasks_def) = &def.tasks else {
return quote!();
};
let default_task_enum = TaskEnumDef::generate(&tasks_def, def);
let task_enum = def.task_enum.as_ref().unwrap_or_else(|| &default_task_enum);
let tasks_expansion = tasks_def.expand_to_tokens(def);
let task_enum_expansion = task_enum.expand_to_tokens(def);
quote! {
#tasks_expansion
#task_enum_expansion
}
}
@@ -0,0 +1,216 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
pallet::{CompositeKeyword, Def},
COUNTER,
};
use syn::spanned::Spanned;
/// Generate the `tt_default_parts` macro.
pub fn expand_tt_default_parts(def: &mut Def) -> proc_macro2::TokenStream {
let count = COUNTER.with(|counter| counter.borrow_mut().inc());
let default_parts_unique_id =
syn::Ident::new(&format!("__tt_default_parts_{}", count), def.item.span());
let extra_parts_unique_id =
syn::Ident::new(&format!("__tt_extra_parts_{}", count), def.item.span());
let default_parts_unique_id_v2 =
syn::Ident::new(&format!("__tt_default_parts_v2_{}", count), def.item.span());
let call_part = def.call.as_ref().map(|_| quote::quote!(Call,));
let task_part = def.tasks.as_ref().map(|_| quote::quote!(Task,));
let storage_part = (!def.storages.is_empty()).then(|| quote::quote!(Storage,));
let event_part = def.event.as_ref().map(|event| {
let gen = event.gen_kind.is_generic().then(|| quote::quote!( <T> ));
quote::quote!( Event #gen , )
});
let error_part = def.error.as_ref().map(|_| quote::quote!(Error<T>,));
let origin_part = def.origin.as_ref().map(|origin| {
let gen = origin.is_generic.then(|| quote::quote!( <T> ));
quote::quote!( Origin #gen , )
});
let config_part = def.genesis_config.as_ref().map(|genesis_config| {
let gen = genesis_config.gen_kind.is_generic().then(|| quote::quote!( <T> ));
quote::quote!( Config #gen , )
});
let inherent_part = def.inherent.as_ref().map(|_| quote::quote!(Inherent,));
let validate_unsigned_part =
def.validate_unsigned.as_ref().map(|_| quote::quote!(ValidateUnsigned,));
let freeze_reason_part = def
.composites
.iter()
.any(|c| matches!(c.composite_keyword, CompositeKeyword::FreezeReason(_)))
.then_some(quote::quote!(FreezeReason,));
let hold_reason_part = def
.composites
.iter()
.any(|c| matches!(c.composite_keyword, CompositeKeyword::HoldReason(_)))
.then_some(quote::quote!(HoldReason,));
let lock_id_part = def
.composites
.iter()
.any(|c| matches!(c.composite_keyword, CompositeKeyword::LockId(_)))
.then_some(quote::quote!(LockId,));
let slash_reason_part = def
.composites
.iter()
.any(|c| matches!(c.composite_keyword, CompositeKeyword::SlashReason(_)))
.then_some(quote::quote!(SlashReason,));
let call_part_v2 = def.call.as_ref().map(|_| quote::quote!(+ Call));
let task_part_v2 = def.tasks.as_ref().map(|_| quote::quote!(+ Task));
let storage_part_v2 = (!def.storages.is_empty()).then(|| quote::quote!(+ Storage));
let event_part_v2 = def.event.as_ref().map(|event| {
let gen = event.gen_kind.is_generic().then(|| quote::quote!(<T>));
quote::quote!(+ Event #gen)
});
let error_part_v2 = def.error.as_ref().map(|_| quote::quote!(+ Error<T>));
let origin_part_v2 = def.origin.as_ref().map(|origin| {
let gen = origin.is_generic.then(|| quote::quote!(<T>));
quote::quote!(+ Origin #gen)
});
let config_part_v2 = def.genesis_config.as_ref().map(|genesis_config| {
let gen = genesis_config.gen_kind.is_generic().then(|| quote::quote!(<T>));
quote::quote!(+ Config #gen)
});
let inherent_part_v2 = def.inherent.as_ref().map(|_| quote::quote!(+ Inherent));
let validate_unsigned_part_v2 =
def.validate_unsigned.as_ref().map(|_| quote::quote!(+ ValidateUnsigned));
let freeze_reason_part_v2 = def
.composites
.iter()
.any(|c| matches!(c.composite_keyword, CompositeKeyword::FreezeReason(_)))
.then_some(quote::quote!(+ FreezeReason));
let hold_reason_part_v2 = def
.composites
.iter()
.any(|c| matches!(c.composite_keyword, CompositeKeyword::HoldReason(_)))
.then_some(quote::quote!(+ HoldReason));
let lock_id_part_v2 = def
.composites
.iter()
.any(|c| matches!(c.composite_keyword, CompositeKeyword::LockId(_)))
.then_some(quote::quote!(+ LockId));
let slash_reason_part_v2 = def
.composites
.iter()
.any(|c| matches!(c.composite_keyword, CompositeKeyword::SlashReason(_)))
.then_some(quote::quote!(+ SlashReason));
quote::quote!(
// This macro follows the conventions as laid out by the `tt-call` crate. It does not
// accept any arguments and simply returns the pallet parts, separated by commas, then
// wrapped inside of braces and finally prepended with double colons, to the caller inside
// of a key named `tokens`.
//
// We need to accept a path argument here, because this macro gets expanded on the
// crate that called the `construct_runtime!` macro, and the actual path is unknown.
#[macro_export]
#[doc(hidden)]
macro_rules! #default_parts_unique_id {
{
$caller:tt
your_tt_return = [{ $my_tt_return:path }]
} => {
$my_tt_return! {
$caller
tokens = [{
expanded::{
Pallet, #call_part #storage_part #event_part #error_part #origin_part #config_part
#inherent_part #validate_unsigned_part #freeze_reason_part #task_part
#hold_reason_part #lock_id_part #slash_reason_part
}
}]
}
};
}
pub use #default_parts_unique_id as tt_default_parts;
// This macro is similar to the `tt_default_parts!`. It expands the pallets that are declared
// explicitly (`System: pezframe_system::{Pallet, Call}`) with extra parts.
//
// For example, after expansion an explicit pallet would look like:
// `System: expanded::{Error} ::{Pallet, Call}`.
//
// The `expanded` keyword is a marker of the final state of the `construct_runtime!`.
#[macro_export]
#[doc(hidden)]
macro_rules! #extra_parts_unique_id {
{
$caller:tt
your_tt_return = [{ $my_tt_return:path }]
} => {
$my_tt_return! {
$caller
tokens = [{
expanded::{
#error_part
}
}]
}
};
}
pub use #extra_parts_unique_id as tt_extra_parts;
#[macro_export]
#[doc(hidden)]
macro_rules! #default_parts_unique_id_v2 {
{
$caller:tt
your_tt_return = [{ $my_tt_return:path }]
} => {
$my_tt_return! {
$caller
tokens = [{
+ Pallet #call_part_v2 #storage_part_v2 #event_part_v2 #error_part_v2 #origin_part_v2 #config_part_v2
#inherent_part_v2 #validate_unsigned_part_v2 #freeze_reason_part_v2 #task_part_v2
#hold_reason_part_v2 #lock_id_part_v2 #slash_reason_part_v2
}]
}
};
}
pub use #default_parts_unique_id_v2 as tt_default_parts_v2;
)
}
@@ -0,0 +1,77 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::pallet::Def;
///
/// * Generate the struct
/// * implement the `Get<..>` on it
/// * Rename the name of the function to internal name
pub fn expand_type_values(def: &mut Def) -> proc_macro2::TokenStream {
let mut expand = quote::quote!();
let pezframe_support = &def.pezframe_support;
for type_value in &def.type_values {
let fn_name_str = &type_value.ident.to_string();
let fn_name_snakecase = inflector::cases::snakecase::to_snake_case(fn_name_str);
let fn_ident_renamed = syn::Ident::new(
&format!("__type_value_for_{}", fn_name_snakecase),
type_value.ident.span(),
);
let type_value_item = {
let item = &mut def.item.content.as_mut().expect("Checked by def").1[type_value.index];
if let syn::Item::Fn(item) = item {
item
} else {
unreachable!("Checked by error parser")
}
};
// Rename the type_value function name
type_value_item.sig.ident = fn_ident_renamed.clone();
let vis = &type_value.vis;
let ident = &type_value.ident;
let type_ = &type_value.type_;
let where_clause = &type_value.where_clause;
let (struct_impl_gen, struct_use_gen) = if type_value.is_generic {
(
def.type_impl_generics(type_value.attr_span),
def.type_use_generics(type_value.attr_span),
)
} else {
(Default::default(), Default::default())
};
let docs = &type_value.docs;
expand.extend(quote::quote_spanned!(type_value.attr_span =>
#( #[doc = #docs] )*
#vis struct #ident<#struct_use_gen>(core::marker::PhantomData<((), #struct_use_gen)>);
impl<#struct_impl_gen> #pezframe_support::traits::Get<#type_> for #ident<#struct_use_gen>
#where_clause
{
fn get() -> #type_ {
#fn_ident_renamed::<#struct_use_gen>()
}
}
));
}
expand
}
@@ -0,0 +1,56 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{pallet::Def, COUNTER};
use proc_macro2::TokenStream;
use quote::quote;
use syn::{spanned::Spanned, Ident};
pub fn expand_validate_unsigned(def: &mut Def) -> TokenStream {
let count = COUNTER.with(|counter| counter.borrow_mut().inc());
let macro_ident =
Ident::new(&format!("__is_validate_unsigned_part_defined_{}", count), def.item.span());
let maybe_compile_error = if def.validate_unsigned.is_none() {
quote! {
compile_error!(concat!(
"`",
stringify!($pezpallet_name),
"` does not have #[pallet::validate_unsigned] defined, perhaps you should \
remove `ValidateUnsigned` from construct_runtime?",
));
}
} else {
TokenStream::new()
};
quote! {
#[doc(hidden)]
pub mod __bizinikiwi_validate_unsigned_check {
#[macro_export]
#[doc(hidden)]
macro_rules! #macro_ident {
($pezpallet_name:ident) => {
#maybe_compile_error
}
}
#[doc(hidden)]
pub use #macro_ident as is_validate_unsigned_part_defined;
}
}
}
@@ -0,0 +1,260 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::pallet::{parse::view_functions::ViewFunctionDef, Def};
use proc_macro2::{Span, TokenStream};
use syn::spanned::Spanned;
pub fn expand_view_functions(def: &Def) -> TokenStream {
let (span, where_clause, view_fns) = match def.view_functions.as_ref() {
Some(view_fns) =>
(view_fns.attr_span, view_fns.where_clause.clone(), view_fns.view_functions.clone()),
None => (def.item.span(), def.config.where_clause.clone(), Vec::new()),
};
let view_function_prefix_impl =
expand_view_function_prefix_impl(def, span, where_clause.as_ref());
let view_fn_impls = view_fns
.iter()
.map(|view_fn| expand_view_function(def, span, where_clause.as_ref(), view_fn));
let impl_dispatch_view_function =
impl_dispatch_view_function(def, span, where_clause.as_ref(), &view_fns);
let impl_view_function_metadata =
impl_view_function_metadata(def, span, where_clause.as_ref(), &view_fns);
quote::quote! {
#view_function_prefix_impl
#( #view_fn_impls )*
#impl_dispatch_view_function
#impl_view_function_metadata
}
}
fn expand_view_function_prefix_impl(
def: &Def,
span: Span,
where_clause: Option<&syn::WhereClause>,
) -> TokenStream {
let pezpallet_ident = &def.pezpallet_struct.pallet;
let pezframe_support = &def.pezframe_support;
let pezframe_system = &def.pezframe_system;
let type_impl_gen = &def.type_impl_generics(span);
let type_use_gen = &def.type_use_generics(span);
quote::quote! {
impl<#type_impl_gen> #pezframe_support::view_functions::ViewFunctionIdPrefix for #pezpallet_ident<#type_use_gen> #where_clause {
fn prefix() -> [::core::primitive::u8; 16usize] {
<
<T as #pezframe_system::Config>::PalletInfo
as #pezframe_support::traits::PalletInfo
>::name_hash::<Pallet<#type_use_gen>>()
.expect("No name_hash found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`.")
}
}
}
}
fn expand_view_function(
def: &Def,
span: Span,
where_clause: Option<&syn::WhereClause>,
view_fn: &ViewFunctionDef,
) -> TokenStream {
let pezframe_support = &def.pezframe_support;
let pezpallet_ident = &def.pezpallet_struct.pallet;
let type_impl_gen = &def.type_impl_generics(span);
let type_decl_bounded_gen = &def.type_decl_bounded_generics(span);
let type_use_gen = &def.type_use_generics(span);
let capture_docs = if cfg!(feature = "no-metadata-docs") { "never" } else { "always" };
let view_function_struct_ident = view_fn.view_function_struct_ident();
let view_fn_name = &view_fn.name;
let (arg_names, arg_types) = match view_fn.args_names_types() {
Ok((arg_names, arg_types)) => (arg_names, arg_types),
Err(e) => return e.into_compile_error(),
};
let return_type = &view_fn.return_type;
let docs = &view_fn.docs;
let view_function_id_suffix_bytes_raw = match view_fn.view_function_id_suffix_bytes() {
Ok(view_function_id_suffix_bytes_raw) => view_function_id_suffix_bytes_raw,
Err(e) => return e.into_compile_error(),
};
let view_function_id_suffix_bytes = view_function_id_suffix_bytes_raw
.map(|byte| syn::LitInt::new(&format!("0x{:X}_u8", byte), Span::call_site()));
quote::quote! {
#( #[doc = #docs] )*
#[allow(missing_docs)]
#[derive(
#pezframe_support::RuntimeDebugNoBound,
#pezframe_support::CloneNoBound,
#pezframe_support::EqNoBound,
#pezframe_support::PartialEqNoBound,
#pezframe_support::__private::codec::Encode,
#pezframe_support::__private::codec::Decode,
#pezframe_support::__private::codec::DecodeWithMemTracking,
#pezframe_support::__private::scale_info::TypeInfo,
)]
#[codec(encode_bound())]
#[codec(decode_bound())]
#[scale_info(skip_type_params(#type_use_gen), capture_docs = #capture_docs)]
pub struct #view_function_struct_ident<#type_decl_bounded_gen> #where_clause {
#(
pub #arg_names: #arg_types,
)*
_marker: ::core::marker::PhantomData<(#type_use_gen,)>,
}
impl<#type_impl_gen> #view_function_struct_ident<#type_use_gen> #where_clause {
/// Create a new [`#view_function_struct_ident`] instance.
pub fn new(#( #arg_names: #arg_types, )*) -> Self {
Self {
#( #arg_names, )*
_marker: ::core::default::Default::default()
}
}
}
impl<#type_impl_gen> #pezframe_support::view_functions::ViewFunctionIdSuffix for #view_function_struct_ident<#type_use_gen> #where_clause {
const SUFFIX: [::core::primitive::u8; 16usize] = [ #( #view_function_id_suffix_bytes ),* ];
}
impl<#type_impl_gen> #pezframe_support::view_functions::ViewFunction for #view_function_struct_ident<#type_use_gen> #where_clause {
fn id() -> #pezframe_support::view_functions::ViewFunctionId {
#pezframe_support::view_functions::ViewFunctionId {
prefix: <#pezpallet_ident<#type_use_gen> as #pezframe_support::view_functions::ViewFunctionIdPrefix>::prefix(),
suffix: <Self as #pezframe_support::view_functions::ViewFunctionIdSuffix>::SUFFIX,
}
}
type ReturnType = #return_type;
fn invoke(self) -> Self::ReturnType {
let Self { #( #arg_names, )* _marker } = self;
#pezpallet_ident::<#type_use_gen> :: #view_fn_name( #( #arg_names, )* )
}
}
}
}
fn impl_dispatch_view_function(
def: &Def,
span: Span,
where_clause: Option<&syn::WhereClause>,
view_fns: &[ViewFunctionDef],
) -> TokenStream {
let pezframe_support = &def.pezframe_support;
let pezpallet_ident = &def.pezpallet_struct.pallet;
let type_impl_gen = &def.type_impl_generics(span);
let type_use_gen = &def.type_use_generics(span);
let query_match_arms = view_fns.iter().map(|view_fn| {
let view_function_struct_ident = view_fn.view_function_struct_ident();
quote::quote! {
<#view_function_struct_ident<#type_use_gen> as #pezframe_support::view_functions::ViewFunctionIdSuffix>::SUFFIX => {
<#view_function_struct_ident<#type_use_gen> as #pezframe_support::view_functions::ViewFunction>::execute(input, output)
}
}
});
quote::quote! {
impl<#type_impl_gen> #pezframe_support::view_functions::DispatchViewFunction
for #pezpallet_ident<#type_use_gen> #where_clause
{
#[deny(unreachable_patterns)]
fn dispatch_view_function<O: #pezframe_support::__private::codec::Output>(
id: & #pezframe_support::view_functions::ViewFunctionId,
input: &mut &[u8],
output: &mut O
) -> Result<(), #pezframe_support::view_functions::ViewFunctionDispatchError>
{
match id.suffix {
#( #query_match_arms )*
_ => Err(#pezframe_support::view_functions::ViewFunctionDispatchError::NotFound(id.clone())),
}
}
}
}
}
fn impl_view_function_metadata(
def: &Def,
span: Span,
where_clause: Option<&syn::WhereClause>,
view_fns: &[ViewFunctionDef],
) -> TokenStream {
let pezframe_support = &def.pezframe_support;
let pezpallet_ident = &def.pezpallet_struct.pallet;
let type_impl_gen = &def.type_impl_generics(span);
let type_use_gen = &def.type_use_generics(span);
let view_functions = view_fns.iter().map(|view_fn| {
let view_function_struct_ident = view_fn.view_function_struct_ident();
let name = &view_fn.name;
let inputs = view_fn.args.iter().filter_map(|fn_arg| {
match fn_arg {
syn::FnArg::Receiver(_) => None,
syn::FnArg::Typed(typed) => {
let pat = &typed.pat;
let ty = &typed.ty;
Some(quote::quote! {
#pezframe_support::__private::metadata_ir::PalletViewFunctionParamMetadataIR {
name: ::core::stringify!(#pat),
ty: #pezframe_support::__private::scale_info::meta_type::<#ty>(),
}
})
}
}
});
let no_docs = vec![];
let doc = if cfg!(feature = "no-metadata-docs") { &no_docs } else { &view_fn.docs };
let deprecation = match crate::deprecation::get_deprecation(
&quote::quote! { #pezframe_support },
&def.item.attrs,
) {
Ok(deprecation) => deprecation,
Err(e) => return e.into_compile_error(),
};
quote::quote! {
#pezframe_support::__private::metadata_ir::PalletViewFunctionMetadataIR {
name: ::core::stringify!(#name),
id: <#view_function_struct_ident<#type_use_gen> as #pezframe_support::view_functions::ViewFunction>::id().into(),
inputs: #pezframe_support::__private::pezsp_std::vec![ #( #inputs ),* ],
output: #pezframe_support::__private::scale_info::meta_type::<
<#view_function_struct_ident<#type_use_gen> as #pezframe_support::view_functions::ViewFunction>::ReturnType
>(),
docs: #pezframe_support::__private::pezsp_std::vec![ #( #doc ),* ],
deprecation_info: #deprecation,
}
}
});
quote::quote! {
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen> #where_clause {
#[doc(hidden)]
pub fn pezpallet_view_functions_metadata()
-> #pezframe_support::__private::Vec<#pezframe_support::__private::metadata_ir::PalletViewFunctionMetadataIR> {
#pezframe_support::__private::vec![ #( #view_functions ),* ]
}
}
}
}
@@ -0,0 +1,98 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Generates warnings for undesirable pallet code.
use crate::pallet::parse::call::{CallVariantDef, CallWeightDef};
use proc_macro_warning::Warning;
use syn::{
spanned::Spanned,
visit::{self, Visit},
};
/// Warn if any of the call arguments starts with a underscore and is used in a weight formula.
pub(crate) fn weight_witness_warning(
method: &CallVariantDef,
dev_mode: bool,
warnings: &mut Vec<Warning>,
) {
if dev_mode {
return;
}
let CallWeightDef::Immediate(w) = &method.weight else { return };
let partial_warning = Warning::new_deprecated("UncheckedWeightWitness")
.old("not check weight witness data")
.new("ensure that all witness data for weight calculation is checked before usage")
.help_link("https://github.com/pezkuwichain/kurdistan-sdk/issues/108");
for (_, arg_ident, _) in method.args.iter() {
if !arg_ident.to_string().starts_with('_') || !contains_ident(w.clone(), &arg_ident) {
continue;
}
let warning = partial_warning
.clone()
.index(warnings.len())
.span(arg_ident.span())
.build_or_panic();
warnings.push(warning);
}
}
/// Warn if the weight is a constant and the pallet not in `dev_mode`.
pub(crate) fn weight_constant_warning(
weight: &syn::Expr,
dev_mode: bool,
warnings: &mut Vec<Warning>,
) {
if dev_mode {
return;
}
let syn::Expr::Lit(lit) = weight else { return };
let warning = Warning::new_deprecated("ConstantWeight")
.index(warnings.len())
.old("use hard-coded constant as call weight")
.new("benchmark all calls or put the pallet into `dev` mode")
.help_link("https://github.com/pezkuwichain/kurdistan-sdk/issues/48")
.span(lit.span())
.build_or_panic();
warnings.push(warning);
}
/// Returns whether `expr` contains `ident`.
fn contains_ident(mut expr: syn::Expr, ident: &syn::Ident) -> bool {
struct ContainsIdent {
ident: syn::Ident,
found: bool,
}
impl<'a> Visit<'a> for ContainsIdent {
fn visit_ident(&mut self, i: &syn::Ident) {
if *i == self.ident {
self.found = true;
}
}
}
let mut visitor = ContainsIdent { ident: ident.clone(), found: false };
visit::visit_expr(&mut visitor, &mut expr);
visitor.found
}
@@ -0,0 +1,61 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Implementation for pallet attribute macro.
//!
//! General workflow:
//! 1 - parse all pallet attributes:
//! This step remove all attributes `#[pallet::*]` from the ItemMod and build the `Def` struct
//! which holds the ItemMod without `#[pallet::*]` and information given by those attributes
//! 2 - expand from the parsed information
//! This step will modify the ItemMod by adding some derive attributes or phantom data variants
//! to user defined types. And also crate new types and implement block.
mod expand;
pub(crate) mod parse;
pub use parse::{composite::keyword::CompositeKeyword, Def};
use syn::spanned::Spanned;
mod keyword {
syn::custom_keyword!(dev_mode);
}
pub fn pallet(
attr: proc_macro::TokenStream,
item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let mut dev_mode = false;
if !attr.is_empty() {
if let Ok(_) = syn::parse::<keyword::dev_mode>(attr.clone()) {
dev_mode = true;
} else {
let msg = "Invalid pallet macro call: unexpected attribute. Macro call must be \
bare, such as `#[pezframe_support::pallet]` or `#[pallet]`, or must specify the \
`dev_mode` attribute, such as `#[pezframe_support::pallet(dev_mode)]` or \
#[pallet(dev_mode)].";
let span = proc_macro2::TokenStream::from(attr).span();
return syn::Error::new(span, msg).to_compile_error().into();
}
}
let item = syn::parse_macro_input!(item as syn::ItemMod);
match parse::Def::try_from(item, dev_mode) {
Ok(def) => expand::expand(def).into(),
Err(e) => e.to_compile_error().into(),
}
}
@@ -0,0 +1,537 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::{helper, InheritedCallWeightAttr};
use pezframe_support_procedural_tools::get_doc_literals;
use proc_macro2::Span;
use quote::ToTokens;
use std::collections::HashMap;
use syn::spanned::Spanned;
/// List of additional token to be used for parsing.
mod keyword {
syn::custom_keyword!(Call);
syn::custom_keyword!(OriginFor);
syn::custom_keyword!(RuntimeOrigin);
syn::custom_keyword!(weight);
syn::custom_keyword!(call_index);
syn::custom_keyword!(compact);
syn::custom_keyword!(T);
syn::custom_keyword!(pallet);
syn::custom_keyword!(feeless_if);
syn::custom_keyword!(authorize);
syn::custom_keyword!(weight_of_authorize);
}
/// Definition of dispatchables typically `impl<T: Config> Pallet<T> { ... }`
pub struct CallDef {
/// The where_clause used.
pub where_clause: Option<syn::WhereClause>,
/// A set of usage of instance, must be check for consistency with trait.
pub instances: Vec<helper::InstanceUsage>,
/// The index of call item in pallet module.
pub index: usize,
/// Information on methods (used for expansion).
pub methods: Vec<CallVariantDef>,
/// The span of the pallet::call attribute.
pub attr_span: proc_macro2::Span,
/// Docs, specified on the impl Block.
pub docs: Vec<syn::Expr>,
}
/// The weight of a call or the weight of authorize.
#[derive(Clone)]
pub enum CallWeightDef {
/// Explicitly set on the call itself with `#[pallet::weight(…)]` or
/// `#[pallet::weight_of_authorize(…)]`. This value is used.
Immediate(syn::Expr),
/// The default value that should be set for dev-mode pallets. Usually zero.
DevModeDefault,
/// Inherits whatever value is configured on the pallet level.
///
/// The concrete value is not known at this point.
Inherited(syn::Type),
}
impl CallWeightDef {
fn try_from(
weight: Option<syn::Expr>,
inherited_call_weight: &Option<InheritedCallWeightAttr>,
dev_mode: bool,
) -> Option<Self> {
match (weight, inherited_call_weight) {
(Some(weight), _) => Some(CallWeightDef::Immediate(weight)),
(None, Some(inherited)) => Some(CallWeightDef::Inherited(inherited.typename.clone())),
(None, _) if dev_mode => Some(CallWeightDef::DevModeDefault),
(None, _) => None,
}
}
}
/// Definition of dispatchable typically: `#[weight...] fn foo(origin .., param1: ...) -> ..`
#[derive(Clone)]
pub struct CallVariantDef {
/// Function name.
pub name: syn::Ident,
/// Information on args: `(is_compact, name, type)`
pub args: Vec<(bool, syn::Ident, Box<syn::Type>)>,
/// Weight for the call.
pub weight: CallWeightDef,
/// Call index of the dispatchable.
pub call_index: u8,
/// Whether an explicit call index was specified.
pub explicit_call_index: bool,
/// Docs, used for metadata.
pub docs: Vec<syn::Expr>,
/// Attributes annotated at the top of the dispatchable function.
pub attrs: Vec<syn::Attribute>,
/// The `cfg` attributes.
pub cfg_attrs: Vec<syn::Attribute>,
/// The optional `feeless_if` attribute on the `pallet::call`.
pub feeless_check: Option<syn::ExprClosure>,
/// The return type of the call: `DispatchInfo` or `DispatchResultWithPostInfo`.
pub return_type: helper::CallReturnType,
/// The information related to `authorize` attribute.
/// `(authorize expression, weight of authorize)`
pub authorize: Option<AuthorizeDef>,
}
/// Definition related to the `authorize` attribute and other related attributes.
#[derive(Clone)]
pub struct AuthorizeDef {
/// The expression of the authorize attribute.
pub expr: syn::Expr,
/// The weight of the authorize attribute as define by the attribute
/// `[pallet::weight_of_authorize]`.
pub weight: CallWeightDef,
}
/// Attributes for functions in call impl block.
pub enum FunctionAttr {
/// Parse for `#[pallet::call_index(expr)]`
CallIndex(u8),
/// Parse for `#[pallet::weight(expr)]`
Weight(syn::Expr),
/// Parse for `#[pallet::feeless_if(expr)]`
FeelessIf(Span, syn::ExprClosure),
/// Parse for `#[pallet::authorize(expr)]`
Authorize(syn::Expr),
/// Parse for `#[pallet::weight_of_authorize(expr)]`
WeightOfAuthorize(syn::Expr),
}
impl syn::parse::Parse for FunctionAttr {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
input.parse::<syn::Token![#]>()?;
let content;
syn::bracketed!(content in input);
content.parse::<keyword::pallet>()?;
content.parse::<syn::Token![::]>()?;
let lookahead = content.lookahead1();
if lookahead.peek(keyword::weight) {
content.parse::<keyword::weight>()?;
let weight_content;
syn::parenthesized!(weight_content in content);
Ok(FunctionAttr::Weight(weight_content.parse::<syn::Expr>()?))
} else if lookahead.peek(keyword::call_index) {
content.parse::<keyword::call_index>()?;
let call_index_content;
syn::parenthesized!(call_index_content in content);
let index = call_index_content.parse::<syn::LitInt>()?;
if !index.suffix().is_empty() {
let msg = "Number literal must not have a suffix";
return Err(syn::Error::new(index.span(), msg));
}
Ok(FunctionAttr::CallIndex(index.base10_parse()?))
} else if lookahead.peek(keyword::feeless_if) {
content.parse::<keyword::feeless_if>()?;
let closure_content;
syn::parenthesized!(closure_content in content);
Ok(FunctionAttr::FeelessIf(
closure_content.span(),
closure_content.parse::<syn::ExprClosure>().map_err(|e| {
let msg = "Invalid feeless_if attribute: expected a closure";
let mut err = syn::Error::new(closure_content.span(), msg);
err.combine(e);
err
})?,
))
} else if lookahead.peek(keyword::authorize) {
content.parse::<keyword::authorize>()?;
let closure_content;
syn::parenthesized!(closure_content in content);
Ok(FunctionAttr::Authorize(closure_content.parse::<syn::Expr>()?))
} else if lookahead.peek(keyword::weight_of_authorize) {
content.parse::<keyword::weight_of_authorize>()?;
let closure_content;
syn::parenthesized!(closure_content in content);
Ok(FunctionAttr::WeightOfAuthorize(closure_content.parse::<syn::Expr>()?))
} else {
Err(lookahead.error())
}
}
}
/// Attribute for arguments in function in call impl block.
/// Parse for `#[pallet::compact]|
pub struct ArgAttrIsCompact;
impl syn::parse::Parse for ArgAttrIsCompact {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
input.parse::<syn::Token![#]>()?;
let content;
syn::bracketed!(content in input);
content.parse::<keyword::pallet>()?;
content.parse::<syn::Token![::]>()?;
content.parse::<keyword::compact>()?;
Ok(ArgAttrIsCompact)
}
}
/// Check the syntax is `OriginFor<T>`, `&OriginFor<T>` or `T::RuntimeOrigin`.
pub fn check_dispatchable_first_arg_type(ty: &syn::Type, is_ref: bool) -> syn::Result<()> {
pub struct CheckOriginFor(bool);
impl syn::parse::Parse for CheckOriginFor {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let is_ref = input.parse::<syn::Token![&]>().is_ok();
input.parse::<keyword::OriginFor>()?;
input.parse::<syn::Token![<]>()?;
input.parse::<keyword::T>()?;
input.parse::<syn::Token![>]>()?;
Ok(Self(is_ref))
}
}
pub struct CheckRuntimeOrigin;
impl syn::parse::Parse for CheckRuntimeOrigin {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
input.parse::<keyword::T>()?;
input.parse::<syn::Token![::]>()?;
input.parse::<keyword::RuntimeOrigin>()?;
Ok(Self)
}
}
let result_origin_for = syn::parse2::<CheckOriginFor>(ty.to_token_stream());
let result_runtime_origin = syn::parse2::<CheckRuntimeOrigin>(ty.to_token_stream());
return match (result_origin_for, result_runtime_origin) {
(Ok(CheckOriginFor(has_ref)), _) if is_ref == has_ref => Ok(()),
(_, Ok(_)) => Ok(()),
(_, _) => {
let msg = if is_ref {
"Invalid type: expected `&OriginFor<T>`"
} else {
"Invalid type: expected `OriginFor<T>` or `T::RuntimeOrigin`"
};
return Err(syn::Error::new(ty.span(), msg));
},
};
}
impl CallDef {
pub fn try_from(
attr_span: proc_macro2::Span,
index: usize,
item: &mut syn::Item,
dev_mode: bool,
inherited_call_weight: Option<InheritedCallWeightAttr>,
) -> syn::Result<Self> {
let item_impl = if let syn::Item::Impl(item) = item {
item
} else {
return Err(syn::Error::new(item.span(), "Invalid pallet::call, expected item impl"));
};
crate::deprecation::prevent_deprecation_attr_on_outer_enum(&item_impl.attrs)?;
let instances = vec![
helper::check_impl_gen(&item_impl.generics, item_impl.impl_token.span())?,
helper::check_pallet_struct_usage(&item_impl.self_ty)?,
];
if let Some((_, _, for_)) = item_impl.trait_ {
let msg = "Invalid pallet::call, expected no trait ident as in \
`impl<..> Pallet<..> { .. }`";
return Err(syn::Error::new(for_.span(), msg));
}
let mut methods = vec![];
let mut indices = HashMap::new();
let mut last_index: Option<u8> = None;
for item in &mut item_impl.items {
if let syn::ImplItem::Fn(method) = item {
if !matches!(method.vis, syn::Visibility::Public(_)) {
let msg = "Invalid pallet::call, dispatchable function must be public: \
`pub fn`";
let span = match method.vis {
syn::Visibility::Inherited => method.sig.span(),
_ => method.vis.span(),
};
return Err(syn::Error::new(span, msg));
}
match method.sig.inputs.first() {
None => {
let msg = "Invalid pallet::call, must have at least origin arg";
return Err(syn::Error::new(method.sig.span(), msg));
},
Some(syn::FnArg::Receiver(_)) => {
let msg = "Invalid pallet::call, first argument must be a typed argument, \
e.g. `origin: OriginFor<T>`";
return Err(syn::Error::new(method.sig.span(), msg));
},
Some(syn::FnArg::Typed(arg)) => {
check_dispatchable_first_arg_type(&arg.ty, false)?;
},
}
let return_type = helper::check_pallet_call_return_type(&method.sig)?;
let cfg_attrs: Vec<syn::Attribute> = helper::get_item_cfg_attrs(&method.attrs);
let mut call_index = None;
let mut weight = None;
let mut feeless_check = None;
let mut authorize = None;
let mut weight_of_authorize = None;
for attr in helper::take_item_pallet_attrs(&mut method.attrs)?.into_iter() {
match attr {
FunctionAttr::CallIndex(idx) => {
if call_index.is_some() {
let msg =
"Invalid pallet::call, too many call_index attributes given";
return Err(syn::Error::new(method.sig.span(), msg));
}
call_index = Some(idx);
},
FunctionAttr::Weight(w) => {
if weight.is_some() {
let msg = "Invalid pallet::call, too many weight attributes given";
return Err(syn::Error::new(method.sig.span(), msg));
}
weight = Some(w);
},
FunctionAttr::FeelessIf(span, closure) => {
if feeless_check.is_some() {
let msg =
"Invalid pallet::call, there can only be one feeless_if attribute";
return Err(syn::Error::new(span, msg));
}
feeless_check = Some(closure);
},
FunctionAttr::Authorize(expr) => {
if authorize.is_some() {
let msg =
"Invalid pallet::call, there can only be one authorize attribute";
return Err(syn::Error::new(method.sig.span(), msg));
}
authorize = Some(expr);
},
FunctionAttr::WeightOfAuthorize(expr) => {
if weight_of_authorize.is_some() {
let msg = "Invalid pallet::call, there can only be one weight_of_authorize attribute";
return Err(syn::Error::new(method.sig.span(), msg));
}
weight_of_authorize = Some(expr);
},
}
}
if weight_of_authorize.is_some() && authorize.is_none() {
let msg = "Invalid pallet::call, weight_of_authorize attribute must be used with authorize attribute";
return Err(syn::Error::new(weight_of_authorize.unwrap().span(), msg));
}
let authorize = if let Some(expr) = authorize {
let weight_of_authorize = CallWeightDef::try_from(
weight_of_authorize,
&inherited_call_weight,
dev_mode,
)
.ok_or_else(|| {
syn::Error::new(
method.sig.span(),
"A pallet::call using authorize requires either a concrete \
`#[pallet::weight_of_authorize($expr)]` or an inherited weight from \
the `#[pallet:call(weight($type))]` attribute, but \
none were given.",
)
})?;
Some(AuthorizeDef { expr, weight: weight_of_authorize })
} else {
None
};
let weight = CallWeightDef::try_from(weight, &inherited_call_weight, dev_mode)
.ok_or_else(|| {
syn::Error::new(
method.sig.span(),
"A pallet::call requires either a concrete `#[pallet::weight($expr)]` \
or an inherited weight from the `#[pallet:call(weight($type))]` \
attribute, but none were given.",
)
})?;
let explicit_call_index = call_index.is_some();
let final_index = match call_index {
Some(i) => i,
None =>
last_index.map_or(Some(0), |idx| idx.checked_add(1)).ok_or_else(|| {
let msg = "Call index doesn't fit into u8, index is 256";
syn::Error::new(method.sig.span(), msg)
})?,
};
last_index = Some(final_index);
if let Some(used_fn) = indices.insert(final_index, method.sig.ident.clone()) {
let msg = format!(
"Call indices are conflicting: Both functions {} and {} are at index {}",
used_fn, method.sig.ident, final_index,
);
let mut err = syn::Error::new(used_fn.span(), &msg);
err.combine(syn::Error::new(method.sig.ident.span(), msg));
return Err(err);
}
let mut args = vec![];
for arg in method.sig.inputs.iter_mut().skip(1) {
let arg = if let syn::FnArg::Typed(arg) = arg {
arg
} else {
unreachable!("Only first argument can be receiver");
};
let arg_attrs: Vec<ArgAttrIsCompact> =
helper::take_item_pallet_attrs(&mut arg.attrs)?;
if arg_attrs.len() > 1 {
let msg = "Invalid pallet::call, argument has too many attributes";
return Err(syn::Error::new(arg.span(), msg));
}
let arg_ident = if let syn::Pat::Ident(pat) = &*arg.pat {
pat.ident.clone()
} else {
let msg = "Invalid pallet::call, argument must be ident";
return Err(syn::Error::new(arg.pat.span(), msg));
};
args.push((!arg_attrs.is_empty(), arg_ident, arg.ty.clone()));
}
let docs = get_doc_literals(&method.attrs);
if let Some(ref feeless_check) = feeless_check {
if feeless_check.inputs.len() != args.len() + 1 {
let msg = "Invalid pallet::call, feeless_if closure must have same \
number of arguments as the dispatchable function";
return Err(syn::Error::new(feeless_check.span(), msg));
}
match feeless_check.inputs.first() {
None => {
let msg = "Invalid pallet::call, feeless_if closure must have at least origin arg";
return Err(syn::Error::new(feeless_check.span(), msg));
},
Some(syn::Pat::Type(arg)) => {
check_dispatchable_first_arg_type(&arg.ty, true)?;
},
_ => {
let msg = "Invalid pallet::call, feeless_if closure first argument must be a typed argument, \
e.g. `origin: OriginFor<T>`";
return Err(syn::Error::new(feeless_check.span(), msg));
},
}
for (feeless_arg, arg) in feeless_check.inputs.iter().skip(1).zip(args.iter()) {
let feeless_arg_type = if let syn::Pat::Type(syn::PatType { ty, .. }) =
feeless_arg.clone()
{
if let syn::Type::Reference(pat) = *ty {
pat.elem.clone()
} else {
let msg = "Invalid pallet::call, feeless_if closure argument must be a reference";
return Err(syn::Error::new(ty.span(), msg));
}
} else {
let msg = "Invalid pallet::call, feeless_if closure argument must be a type ascription pattern";
return Err(syn::Error::new(feeless_arg.span(), msg));
};
if feeless_arg_type != arg.2 {
let msg =
"Invalid pallet::call, feeless_if closure argument must have \
a reference to the same type as the dispatchable function argument";
return Err(syn::Error::new(feeless_arg.span(), msg));
}
}
let valid_return = match &feeless_check.output {
syn::ReturnType::Type(_, type_) => match *(type_.clone()) {
syn::Type::Path(syn::TypePath { path, .. }) => path.is_ident("bool"),
_ => false,
},
_ => false,
};
if !valid_return {
let msg = "Invalid pallet::call, feeless_if closure must return `bool`";
return Err(syn::Error::new(feeless_check.output.span(), msg));
}
}
methods.push(CallVariantDef {
name: method.sig.ident.clone(),
weight,
call_index: final_index,
explicit_call_index,
args,
docs,
attrs: method.attrs.clone(),
cfg_attrs,
feeless_check,
return_type,
authorize,
});
} else {
let msg = "Invalid pallet::call, only method accepted";
return Err(syn::Error::new(item.span(), msg));
}
}
Ok(Self {
index,
attr_span,
instances,
methods,
where_clause: item_impl.generics.where_clause.clone(),
docs: get_doc_literals(&item_impl.attrs),
})
}
}
@@ -0,0 +1,190 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::helper;
use quote::ToTokens;
use syn::spanned::Spanned;
pub mod keyword {
use super::*;
syn::custom_keyword!(FreezeReason);
syn::custom_keyword!(HoldReason);
syn::custom_keyword!(LockId);
syn::custom_keyword!(SlashReason);
syn::custom_keyword!(Task);
pub enum CompositeKeyword {
FreezeReason(FreezeReason),
HoldReason(HoldReason),
LockId(LockId),
SlashReason(SlashReason),
Task(Task),
}
impl ToTokens for CompositeKeyword {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
use CompositeKeyword::*;
match self {
FreezeReason(inner) => inner.to_tokens(tokens),
HoldReason(inner) => inner.to_tokens(tokens),
LockId(inner) => inner.to_tokens(tokens),
SlashReason(inner) => inner.to_tokens(tokens),
Task(inner) => inner.to_tokens(tokens),
}
}
}
impl syn::parse::Parse for CompositeKeyword {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(FreezeReason) {
Ok(Self::FreezeReason(input.parse()?))
} else if lookahead.peek(HoldReason) {
Ok(Self::HoldReason(input.parse()?))
} else if lookahead.peek(LockId) {
Ok(Self::LockId(input.parse()?))
} else if lookahead.peek(SlashReason) {
Ok(Self::SlashReason(input.parse()?))
} else if lookahead.peek(Task) {
Ok(Self::Task(input.parse()?))
} else {
Err(lookahead.error())
}
}
}
impl std::fmt::Display for CompositeKeyword {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use CompositeKeyword::*;
write!(
f,
"{}",
match self {
FreezeReason(_) => "FreezeReason",
HoldReason(_) => "HoldReason",
Task(_) => "Task",
LockId(_) => "LockId",
SlashReason(_) => "SlashReason",
}
)
}
}
}
pub struct CompositeDef {
/// The composite keyword used (contains span).
pub composite_keyword: keyword::CompositeKeyword,
/// Name of the associated type.
pub ident: syn::Ident,
/// Type parameters and where clause attached to a declaration of the pallet::composite_enum.
pub generics: syn::Generics,
/// The span of the pallet::composite_enum attribute.
pub attr_span: proc_macro2::Span,
/// Variant count of the pallet::composite_enum.
pub variant_count: u32,
}
impl CompositeDef {
pub fn try_from(
attr_span: proc_macro2::Span,
scrate: &syn::Path,
item: &mut syn::Item,
) -> syn::Result<Self> {
let item = if let syn::Item::Enum(item) = item {
// check variants: composite enums support only field-less enum variants. This is
// because fields can introduce too many possibilities, making it challenging to compute
// a fixed variant count.
for variant in &item.variants {
match variant.fields {
syn::Fields::Named(_) | syn::Fields::Unnamed(_) =>
return Err(syn::Error::new(
variant.ident.span(),
"The composite enum does not support variants with fields!",
)),
syn::Fields::Unit => (),
}
}
item
} else {
return Err(syn::Error::new(
item.span(),
"Invalid pallet::composite_enum, expected enum item",
));
};
if !matches!(item.vis, syn::Visibility::Public(_)) {
let msg = format!("Invalid pallet::composite_enum, `{}` must be public", item.ident);
return Err(syn::Error::new(item.span(), msg));
}
let has_instance = if item.generics.params.first().is_some() {
helper::check_config_def_gen(&item.generics, item.ident.span())?;
true
} else {
false
};
let has_derive_attr = item.attrs.iter().any(|attr| {
if let syn::Meta::List(syn::MetaList { path, .. }) = &attr.meta {
path.get_ident().map(|ident| ident == "derive").unwrap_or(false)
} else {
false
}
});
if !has_derive_attr {
let derive_attr: syn::Attribute = syn::parse_quote! {
#[derive(
Copy, Clone, Eq, PartialEq,
#scrate::__private::codec::Encode,
#scrate::__private::codec::Decode,
#scrate::__private::codec::DecodeWithMemTracking,
#scrate::__private::codec::MaxEncodedLen,
#scrate::__private::scale_info::TypeInfo,
#scrate::__private::RuntimeDebug,
)]
};
item.attrs.push(derive_attr);
}
if has_instance {
item.attrs.push(syn::parse_quote! {
#[scale_info(skip_type_params(I))]
});
item.variants.push(syn::parse_quote! {
#[doc(hidden)]
#[codec(skip)]
__Ignore(
::core::marker::PhantomData<I>,
)
});
}
let composite_keyword =
syn::parse2::<keyword::CompositeKeyword>(item.ident.to_token_stream())?;
Ok(CompositeDef {
composite_keyword,
attr_span,
generics: item.generics.clone(),
variant_count: item.variants.len() as u32,
ident: item.ident.clone(),
})
}
}
@@ -0,0 +1,727 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::helper;
use pezframe_support_procedural_tools::{get_cfg_attributes, get_doc_literals, is_using_frame_crate};
use proc_macro_warning::Warning;
use quote::ToTokens;
use syn::{parse_quote, spanned::Spanned, token, Token, TraitItemType};
/// List of additional token to be used for parsing.
mod keyword {
syn::custom_keyword!(Config);
syn::custom_keyword!(From);
syn::custom_keyword!(T);
syn::custom_keyword!(I);
syn::custom_keyword!(config);
syn::custom_keyword!(pallet);
syn::custom_keyword!(IsType);
syn::custom_keyword!(RuntimeEvent);
syn::custom_keyword!(Event);
syn::custom_keyword!(pezframe_system);
syn::custom_keyword!(disable_pezframe_system_supertrait_check);
syn::custom_keyword!(no_default);
syn::custom_keyword!(no_default_bounds);
syn::custom_keyword!(constant);
syn::custom_keyword!(include_metadata);
}
#[derive(Default)]
pub struct DefaultTrait {
/// A bool for each sub-trait item indicates whether the item has
/// `#[pallet::no_default_bounds]` attached to it. If true, the item will not have any bounds
/// in the generated default sub-trait.
pub items: Vec<(syn::TraitItem, bool)>,
pub has_system: bool,
}
/// Input definition for the pallet config.
pub struct ConfigDef {
/// The index of item in pallet module.
pub index: usize,
/// Whether the trait has instance (i.e. define with `Config<I = ()>`)
pub has_instance: bool,
/// Const associated type.
pub consts_metadata: Vec<ConstMetadataDef>,
/// Associated types metadata.
pub associated_types_metadata: Vec<AssociatedTypeMetadataDef>,
/// The where clause on trait definition but modified so `Self` is `T`.
pub where_clause: Option<syn::WhereClause>,
/// Whether a default sub-trait should be generated.
///
/// Contains default sub-trait items (instantiated by `#[pallet::config(with_default)]`).
/// Vec will be empty if `#[pallet::config(with_default)]` is not specified or if there are
/// no trait items.
pub default_sub_trait: Option<DefaultTrait>,
/// Compile time warnings. Mainly for deprecated items.
pub warnings: Vec<Warning>,
}
/// Input definition for an associated type in pallet config.
pub struct AssociatedTypeMetadataDef {
/// Name of the associated type.
pub ident: syn::Ident,
/// The doc associated.
pub doc: Vec<syn::Expr>,
/// The cfg associated.
pub cfg: Vec<syn::Attribute>,
}
impl From<&syn::TraitItemType> for AssociatedTypeMetadataDef {
fn from(trait_ty: &syn::TraitItemType) -> Self {
let ident = trait_ty.ident.clone();
let doc = get_doc_literals(&trait_ty.attrs);
let cfg = get_cfg_attributes(&trait_ty.attrs);
Self { ident, doc, cfg }
}
}
/// Input definition for a constant in pallet config.
pub struct ConstMetadataDef {
/// Name of the associated type.
pub ident: syn::Ident,
/// The type in Get, e.g. `u32` in `type Foo: Get<u32>;`, but `Self` is replaced by `T`
pub type_: syn::Type,
/// The doc associated
pub doc: Vec<syn::Expr>,
/// attributes
pub attrs: Vec<syn::Attribute>,
}
impl TryFrom<&syn::TraitItemType> for ConstMetadataDef {
type Error = syn::Error;
fn try_from(trait_ty: &syn::TraitItemType) -> Result<Self, Self::Error> {
let err = |span, msg| {
syn::Error::new(span, format!("Invalid usage of `#[pallet::constant]`: {}", msg))
};
let doc = get_doc_literals(&trait_ty.attrs);
let ident = trait_ty.ident.clone();
let bound = trait_ty
.bounds
.iter()
.find_map(|param_bound| {
let syn::TypeParamBound::Trait(trait_bound) = param_bound else { return None };
trait_bound.path.segments.last().and_then(|s| (s.ident == "Get").then(|| s))
})
.ok_or_else(|| err(trait_ty.span(), "`Get<T>` trait bound not found"))?;
let syn::PathArguments::AngleBracketed(ref ab) = bound.arguments else {
return Err(err(bound.span(), "Expected trait generic args"));
};
// Only one type argument is expected.
if ab.args.len() != 1 {
return Err(err(bound.span(), "Expected a single type argument"));
}
let syn::GenericArgument::Type(ref type_arg) = ab.args[0] else {
return Err(err(ab.args[0].span(), "Expected a type argument"));
};
let type_ = syn::parse2::<syn::Type>(replace_self_by_t(type_arg.to_token_stream()))
.expect("Internal error: replacing `Self` by `T` should result in valid type");
Ok(Self { ident, type_, doc, attrs: trait_ty.attrs.clone() })
}
}
/// Parse for `#[pallet::disable_pezframe_system_supertrait_check]`
pub struct DisableFrameSystemSupertraitCheck;
impl syn::parse::Parse for DisableFrameSystemSupertraitCheck {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
input.parse::<syn::Token![#]>()?;
let content;
syn::bracketed!(content in input);
content.parse::<syn::Ident>()?;
content.parse::<syn::Token![::]>()?;
content.parse::<keyword::disable_pezframe_system_supertrait_check>()?;
Ok(Self)
}
}
/// Parsing for the `typ` portion of `PalletAttr`
#[derive(derive_syn_parse::Parse, PartialEq, Eq)]
pub enum PalletAttrType {
#[peek(keyword::no_default, name = "no_default")]
NoDefault(keyword::no_default),
#[peek(keyword::no_default_bounds, name = "no_default_bounds")]
NoBounds(keyword::no_default_bounds),
#[peek(keyword::constant, name = "constant")]
Constant(keyword::constant),
#[peek(keyword::include_metadata, name = "include_metadata")]
IncludeMetadata(keyword::include_metadata),
}
/// Parsing for `#[pallet::X]`
#[derive(derive_syn_parse::Parse)]
pub struct PalletAttr {
_pound: Token![#],
#[bracket]
_bracket: token::Bracket,
#[inside(_bracket)]
_pallet: keyword::pallet,
#[prefix(Token![::] in _bracket)]
#[inside(_bracket)]
typ: PalletAttrType,
}
/// Parse for `IsType<<Self as $path>::RuntimeEvent>` and retrieve `$path`
pub struct IsTypeBoundEventParse(syn::Path);
impl syn::parse::Parse for IsTypeBoundEventParse {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
input.parse::<keyword::IsType>()?;
input.parse::<syn::Token![<]>()?;
input.parse::<syn::Token![<]>()?;
input.parse::<syn::Token![Self]>()?;
input.parse::<syn::Token![as]>()?;
let config_path = input.parse::<syn::Path>()?;
input.parse::<syn::Token![>]>()?;
input.parse::<syn::Token![::]>()?;
input.parse::<keyword::RuntimeEvent>()?;
input.parse::<syn::Token![>]>()?;
Ok(Self(config_path))
}
}
/// Parse for `From<Event>` or `From<Event<Self>>` or `From<Event<Self, I>>`
pub struct FromEventParse {
is_generic: bool,
has_instance: bool,
}
impl syn::parse::Parse for FromEventParse {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut is_generic = false;
let mut has_instance = false;
input.parse::<keyword::From>()?;
input.parse::<syn::Token![<]>()?;
input.parse::<keyword::Event>()?;
if input.peek(syn::Token![<]) {
is_generic = true;
input.parse::<syn::Token![<]>()?;
input.parse::<syn::Token![Self]>()?;
if input.peek(syn::Token![,]) {
input.parse::<syn::Token![,]>()?;
input.parse::<keyword::I>()?;
has_instance = true;
}
input.parse::<syn::Token![>]>()?;
}
input.parse::<syn::Token![>]>()?;
Ok(Self { is_generic, has_instance })
}
}
/// Check if trait_item is `type RuntimeEvent`, if so checks its bounds are those expected.
/// (Event type is reserved type)
fn check_event_type(
pezframe_system: &syn::Path,
trait_item: &syn::TraitItem,
trait_has_instance: bool,
) -> syn::Result<bool> {
let syn::TraitItem::Type(type_) = trait_item else { return Ok(false) };
if type_.ident != "RuntimeEvent" {
return Ok(false);
}
// Check event has no generics
if !type_.generics.params.is_empty() || type_.generics.where_clause.is_some() {
let msg =
"Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must have\
no generics nor where_clause";
return Err(syn::Error::new(trait_item.span(), msg));
}
// Check bound contains IsType and From
let has_is_type_bound = type_.bounds.iter().any(|s| {
syn::parse2::<IsTypeBoundEventParse>(s.to_token_stream())
.map_or(false, |b| has_expected_system_config(b.0, pezframe_system))
});
if !has_is_type_bound {
let msg =
"Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must \
bound: `IsType<<Self as pezframe_system::Config>::RuntimeEvent>`"
.to_string();
return Err(syn::Error::new(type_.span(), msg));
}
let from_event_bound = type_
.bounds
.iter()
.find_map(|s| syn::parse2::<FromEventParse>(s.to_token_stream()).ok());
let Some(from_event_bound) = from_event_bound else {
let msg =
"Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must \
bound: `From<Event>` or `From<Event<Self>>` or `From<Event<Self, I>>`";
return Err(syn::Error::new(type_.span(), msg));
};
if from_event_bound.is_generic && (from_event_bound.has_instance != trait_has_instance) {
let msg =
"Invalid `type RuntimeEvent`, associated type `RuntimeEvent` bounds inconsistent \
`From<Event..>`. Config and generic Event must be both with instance or \
without instance";
return Err(syn::Error::new(type_.span(), msg));
}
Ok(true)
}
/// Check that the path to `pezframe_system::Config` is valid, this is that the path is just
/// `pezframe_system::Config` or when using the `frame` crate it is
/// `pezkuwi_sdk_frame::xyz::pezframe_system::Config`.
fn has_expected_system_config(path: syn::Path, pezframe_system: &syn::Path) -> bool {
// Check if `pezframe_system` is actually 'pezframe_system'.
if path.segments.iter().all(|s| s.ident != "pezframe_system") {
return false;
}
let mut expected_system_config =
match (is_using_frame_crate(&path), is_using_frame_crate(&pezframe_system)) {
(true, false) =>
// We can't use the path to `pezframe_system` from `frame` if `pezframe_system` is not being
// in scope through `frame`.
return false,
(false, true) =>
// We know that the only valid pezframe_system path is one that is `pezframe_system`, as
// `frame` re-exports it as such.
syn::parse2::<syn::Path>(quote::quote!(pezframe_system)).expect("is a valid path; qed"),
(_, _) =>
// They are either both `pezframe_system` or both `pezkuwi_sdk_frame::xyz::pezframe_system`.
pezframe_system.clone(),
};
expected_system_config
.segments
.push(syn::PathSegment::from(syn::Ident::new("Config", path.span())));
// the parse path might be something like `pezframe_system::Config<...>`, so we
// only compare the idents along the path.
expected_system_config
.segments
.into_iter()
.map(|ps| ps.ident)
.collect::<Vec<_>>() ==
path.segments.into_iter().map(|ps| ps.ident).collect::<Vec<_>>()
}
/// Replace ident `Self` by `T`
pub fn replace_self_by_t(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
input
.into_iter()
.map(|token_tree| match token_tree {
proc_macro2::TokenTree::Group(group) =>
proc_macro2::Group::new(group.delimiter(), replace_self_by_t(group.stream())).into(),
proc_macro2::TokenTree::Ident(ident) if ident == "Self" =>
proc_macro2::Ident::new("T", ident.span()).into(),
other => other,
})
.collect()
}
/// Check that the trait item requires the `TypeInfo` bound (or similar).
fn contains_type_info_bound(ty: &TraitItemType) -> bool {
const KNOWN_TYPE_INFO_BOUNDS: &[&str] = &[
// Explicit TypeInfo trait.
"TypeInfo",
// Implicit known bizinikiwi traits that implement type info.
// Note: Aim to keep this list as small as possible.
"Parameter",
];
ty.bounds.iter().any(|bound| {
let syn::TypeParamBound::Trait(bound) = bound else { return false };
KNOWN_TYPE_INFO_BOUNDS
.iter()
.any(|known| bound.path.segments.last().map_or(false, |last| last.ident == *known))
})
}
impl ConfigDef {
pub fn try_from(
pezframe_system: &syn::Path,
index: usize,
item: &mut syn::Item,
enable_default: bool,
disable_associated_metadata: bool,
is_pezframe_system: bool,
) -> syn::Result<Self> {
let syn::Item::Trait(item) = item else {
let msg = "Invalid pallet::config, expected trait definition";
return Err(syn::Error::new(item.span(), msg));
};
if !matches!(item.vis, syn::Visibility::Public(_)) {
let msg = "Invalid pallet::config, trait must be public";
return Err(syn::Error::new(item.span(), msg));
}
syn::parse2::<keyword::Config>(item.ident.to_token_stream())?;
let where_clause = {
let stream = replace_self_by_t(item.generics.where_clause.to_token_stream());
syn::parse2::<Option<syn::WhereClause>>(stream).expect(
"Internal error: replacing `Self` by `T` should result in valid where
clause",
)
};
if item.generics.params.len() > 1 {
let msg = "Invalid pallet::config, expected no more than one generic";
return Err(syn::Error::new(item.generics.params[2].span(), msg));
}
let has_instance = if item.generics.params.first().is_some() {
helper::check_config_def_gen(&item.generics, item.ident.span())?;
true
} else {
false
};
let mut consts_metadata = vec![];
let mut associated_types_metadata = vec![];
let mut warnings = vec![];
let mut default_sub_trait = if enable_default {
Some(DefaultTrait { items: Default::default(), has_system: !is_pezframe_system })
} else {
None
};
for trait_item in &mut item.items {
let is_event = check_event_type(pezframe_system, trait_item, has_instance)?;
let mut already_no_default = false;
let mut already_constant = false;
let mut already_no_default_bounds = false;
let mut already_collected_associated_type = None;
// add deprecation notice for `RuntimeEvent`, iff pallet is not `pezframe_system`
if is_event && !is_pezframe_system {
if let syn::TraitItem::Type(type_event) = trait_item {
let allow_dep: syn::Attribute = parse_quote!(#[allow(deprecated)]);
// Check if the `#[allow(deprecated)]` attribute is present
if !type_event.attrs.iter().any(|attr| attr == &allow_dep) {
let warning = Warning::new_deprecated("RuntimeEvent")
.old("have `RuntimeEvent` associated type in the pallet config")
.new("remove it as it is redundant since associated bound gets appended automatically: \n
pub trait Config: pezframe_system::Config<RuntimeEvent: From<Event<Self>>> { }")
.help_link("https://github.com/pezkuwichain/kurdistan-sdk/issues/125")
.span(type_event.ident.span())
.build_or_panic();
warnings.push(warning);
}
}
}
while let Some(pezpallet_attr) =
helper::take_first_item_pallet_attr::<PalletAttr>(trait_item)?
{
match (pezpallet_attr.typ, &trait_item) {
(PalletAttrType::Constant(_), syn::TraitItem::Type(ref typ)) => {
if already_constant {
return Err(syn::Error::new(
pezpallet_attr._bracket.span.join(),
"Duplicate #[pallet::constant] attribute not allowed.",
));
}
already_constant = true;
consts_metadata.push(ConstMetadataDef::try_from(typ)?);
},
(PalletAttrType::Constant(_), _) =>
return Err(syn::Error::new(
trait_item.span(),
"Invalid #[pallet::constant] in #[pallet::config], expected type item",
)),
// Pallet developer has explicitly requested to include metadata for this associated type.
//
// They must provide a type item that implements `TypeInfo`.
(PalletAttrType::IncludeMetadata(_), syn::TraitItem::Type(ref typ)) => {
if already_collected_associated_type.is_some() {
return Err(syn::Error::new(
pezpallet_attr._bracket.span.join(),
"Duplicate #[pallet::include_metadata] attribute not allowed.",
));
}
already_collected_associated_type = Some(pezpallet_attr._bracket.span.join());
associated_types_metadata.push(AssociatedTypeMetadataDef::from(AssociatedTypeMetadataDef::from(typ)));
}
(PalletAttrType::IncludeMetadata(_), _) =>
return Err(syn::Error::new(
pezpallet_attr._bracket.span.join(),
"Invalid #[pallet::include_metadata] in #[pallet::config], expected type item",
)),
(PalletAttrType::NoDefault(_), _) => {
if !enable_default {
return Err(syn::Error::new(
pezpallet_attr._bracket.span.join(),
"`#[pallet::no_default]` can only be used if `#[pallet::config(with_default)]` \
has been specified"
));
}
if already_no_default {
return Err(syn::Error::new(
pezpallet_attr._bracket.span.join(),
"Duplicate #[pallet::no_default] attribute not allowed.",
));
}
already_no_default = true;
},
(PalletAttrType::NoBounds(_), _) => {
if !enable_default {
return Err(syn::Error::new(
pezpallet_attr._bracket.span.join(),
"`#[pallet:no_default_bounds]` can only be used if `#[pallet::config(with_default)]` \
has been specified"
));
}
if already_no_default_bounds {
return Err(syn::Error::new(
pezpallet_attr._bracket.span.join(),
"Duplicate #[pallet::no_default_bounds] attribute not allowed.",
));
}
already_no_default_bounds = true;
},
}
}
if let Some(span) = already_collected_associated_type {
// Events and constants are already propagated to the metadata
if is_event {
return Err(syn::Error::new(
span,
"Invalid #[pallet::include_metadata] for `type RuntimeEvent`. \
The associated type `RuntimeEvent` is already collected in the metadata.",
));
}
if already_constant {
return Err(syn::Error::new(
span,
"Invalid #[pallet::include_metadata]: conflict with #[pallet::constant]. \
Pallet constant already collect the metadata for the type.",
));
}
if let syn::TraitItem::Type(ref ty) = trait_item {
if !contains_type_info_bound(ty) {
let msg = format!(
"Invalid #[pallet::include_metadata] in #[pallet::config], collected type `{}` \
does not implement `TypeInfo` or `Parameter`",
ty.ident,
);
return Err(syn::Error::new(span, msg));
}
}
} else {
// Metadata of associated types is collected by default, if the associated type
// implements `TypeInfo`, or a similar trait that requires the `TypeInfo` bound.
if !disable_associated_metadata && !is_event && !already_constant {
if let syn::TraitItem::Type(ref ty) = trait_item {
// Collect the metadata of the associated type if it implements `TypeInfo`.
if contains_type_info_bound(ty) {
associated_types_metadata.push(AssociatedTypeMetadataDef::from(ty));
}
}
}
}
if !already_no_default && enable_default {
default_sub_trait
.as_mut()
.expect("is 'Some(_)' if 'enable_default'; qed")
.items
.push((trait_item.clone(), already_no_default_bounds));
}
}
let attr: Option<DisableFrameSystemSupertraitCheck> =
helper::take_first_item_pallet_attr(&mut item.attrs)?;
let disable_system_supertrait_check = attr.is_some();
let has_pezframe_system_supertrait = item.supertraits.iter().any(|s| {
syn::parse2::<syn::Path>(s.to_token_stream())
.map_or(false, |b| has_expected_system_config(b, pezframe_system))
});
if !has_pezframe_system_supertrait && !disable_system_supertrait_check {
let found = if item.supertraits.is_empty() {
"none".to_string()
} else {
let mut found = item
.supertraits
.iter()
.fold(String::new(), |acc, s| format!("{}`{}`, ", acc, quote::quote!(#s)));
found.pop();
found.pop();
found
};
let msg = format!(
"Invalid pallet::trait, expected explicit `{}::Config` as supertrait, \
found {}. \
(try `pub trait Config: pezframe_system::Config {{ ...` or \
`pub trait Config<I: 'static>: pezframe_system::Config {{ ...`). \
To disable this check, use `#[pallet::disable_pezframe_system_supertrait_check]`",
pezframe_system.to_token_stream(),
found,
);
return Err(syn::Error::new(item.span(), msg));
}
Ok(Self {
index,
has_instance,
consts_metadata,
associated_types_metadata,
where_clause,
default_sub_trait,
warnings,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn has_expected_system_config_works() {
let pezframe_system = syn::parse2::<syn::Path>(quote::quote!(pezframe_system)).unwrap();
let path = syn::parse2::<syn::Path>(quote::quote!(pezframe_system::Config)).unwrap();
assert!(has_expected_system_config(path, &pezframe_system));
}
#[test]
fn has_expected_system_config_works_with_assoc_type() {
let pezframe_system = syn::parse2::<syn::Path>(quote::quote!(pezframe_system)).unwrap();
let path =
syn::parse2::<syn::Path>(quote::quote!(pezframe_system::Config<RuntimeCall = Call>))
.unwrap();
assert!(has_expected_system_config(path, &pezframe_system));
}
#[test]
fn has_expected_system_config_works_with_frame() {
let path = syn::parse2::<syn::Path>(quote::quote!(pezframe_system::Config)).unwrap();
let pezframe_system =
syn::parse2::<syn::Path>(quote::quote!(pezkuwi_sdk_frame::deps::pezframe_system)).unwrap();
assert!(has_expected_system_config(path.clone(), &pezframe_system));
let pezframe_system =
syn::parse2::<syn::Path>(quote::quote!(frame::deps::pezframe_system)).unwrap();
assert!(has_expected_system_config(path, &pezframe_system));
}
#[test]
fn has_expected_system_config_works_with_frame_full_path() {
let pezframe_system =
syn::parse2::<syn::Path>(quote::quote!(pezkuwi_sdk_frame::deps::pezframe_system)).unwrap();
let path =
syn::parse2::<syn::Path>(quote::quote!(pezkuwi_sdk_frame::deps::pezframe_system::Config))
.unwrap();
assert!(has_expected_system_config(path, &pezframe_system));
let pezframe_system =
syn::parse2::<syn::Path>(quote::quote!(frame::deps::pezframe_system)).unwrap();
let path =
syn::parse2::<syn::Path>(quote::quote!(frame::deps::pezframe_system::Config)).unwrap();
assert!(has_expected_system_config(path, &pezframe_system));
}
#[test]
fn has_expected_system_config_works_with_other_frame_full_path() {
let pezframe_system =
syn::parse2::<syn::Path>(quote::quote!(pezkuwi_sdk_frame::xyz::pezframe_system)).unwrap();
let path =
syn::parse2::<syn::Path>(quote::quote!(pezkuwi_sdk_frame::xyz::pezframe_system::Config))
.unwrap();
assert!(has_expected_system_config(path, &pezframe_system));
let pezframe_system =
syn::parse2::<syn::Path>(quote::quote!(frame::xyz::pezframe_system)).unwrap();
let path =
syn::parse2::<syn::Path>(quote::quote!(frame::xyz::pezframe_system::Config)).unwrap();
assert!(has_expected_system_config(path, &pezframe_system));
}
#[test]
fn has_expected_system_config_does_not_works_with_mixed_frame_full_path() {
let pezframe_system =
syn::parse2::<syn::Path>(quote::quote!(pezkuwi_sdk_frame::xyz::pezframe_system)).unwrap();
let path =
syn::parse2::<syn::Path>(quote::quote!(pezkuwi_sdk_frame::deps::pezframe_system::Config))
.unwrap();
assert!(!has_expected_system_config(path, &pezframe_system));
}
#[test]
fn has_expected_system_config_does_not_works_with_other_mixed_frame_full_path() {
let pezframe_system =
syn::parse2::<syn::Path>(quote::quote!(pezkuwi_sdk_frame::deps::pezframe_system)).unwrap();
let path =
syn::parse2::<syn::Path>(quote::quote!(pezkuwi_sdk_frame::xyz::pezframe_system::Config))
.unwrap();
assert!(!has_expected_system_config(path, &pezframe_system));
}
#[test]
fn has_expected_system_config_does_not_work_with_frame_full_path_if_not_frame_crate() {
let pezframe_system = syn::parse2::<syn::Path>(quote::quote!(pezframe_system)).unwrap();
let path =
syn::parse2::<syn::Path>(quote::quote!(pezkuwi_sdk_frame::deps::pezframe_system::Config))
.unwrap();
assert!(!has_expected_system_config(path, &pezframe_system));
}
#[test]
fn has_expected_system_config_unexpected_pezframe_system() {
let pezframe_system =
syn::parse2::<syn::Path>(quote::quote!(framez::deps::pezframe_system)).unwrap();
let path = syn::parse2::<syn::Path>(quote::quote!(pezframe_system::Config)).unwrap();
assert!(!has_expected_system_config(path, &pezframe_system));
}
#[test]
fn has_expected_system_config_unexpected_path() {
let pezframe_system = syn::parse2::<syn::Path>(quote::quote!(pezframe_system)).unwrap();
let path = syn::parse2::<syn::Path>(quote::quote!(pezframe_system::ConfigSystem)).unwrap();
assert!(!has_expected_system_config(path, &pezframe_system));
}
#[test]
fn has_expected_system_config_not_pezframe_system() {
let pezframe_system = syn::parse2::<syn::Path>(quote::quote!(something)).unwrap();
let path = syn::parse2::<syn::Path>(quote::quote!(something::Config)).unwrap();
assert!(!has_expected_system_config(path, &pezframe_system));
}
}
@@ -0,0 +1,121 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::helper;
use crate::deprecation::extract_or_return_allow_attrs;
use quote::ToTokens;
use syn::{spanned::Spanned, Fields};
/// List of additional token to be used for parsing.
mod keyword {
syn::custom_keyword!(Error);
}
/// Records information about the error enum variant field.
pub struct VariantField {
/// Whether or not the field is named, i.e. whether it is a tuple variant or struct variant.
pub is_named: bool,
}
/// Records information about the error enum variants.
pub struct VariantDef {
/// The variant ident.
pub ident: syn::Ident,
/// The variant field, if any.
pub field: Option<VariantField>,
/// The `cfg` attributes.
pub cfg_attrs: Vec<syn::Attribute>,
/// The `allow` attributes.
pub maybe_allow_attrs: Vec<syn::Attribute>,
}
/// This checks error declaration as a enum declaration with only variants without fields nor
/// discriminant.
pub struct ErrorDef {
/// The index of error item in pallet module.
pub index: usize,
/// Variant definitions.
pub variants: Vec<VariantDef>,
/// A set of usage of instance, must be check for consistency with trait.
pub instances: Vec<helper::InstanceUsage>,
/// The keyword error used (contains span).
pub error: keyword::Error,
/// The span of the pallet::error attribute.
pub attr_span: proc_macro2::Span,
}
impl ErrorDef {
pub fn try_from(
attr_span: proc_macro2::Span,
index: usize,
item: &mut syn::Item,
) -> syn::Result<Self> {
let item = if let syn::Item::Enum(item) = item {
item
} else {
return Err(syn::Error::new(item.span(), "Invalid pallet::error, expected item enum"));
};
if !matches!(item.vis, syn::Visibility::Public(_)) {
let msg = "Invalid pallet::error, `Error` must be public";
return Err(syn::Error::new(item.span(), msg));
}
crate::deprecation::prevent_deprecation_attr_on_outer_enum(&item.attrs)?;
let instances =
vec![helper::check_type_def_gen_no_bounds(&item.generics, item.ident.span())?];
if item.generics.where_clause.is_some() {
let msg = "Invalid pallet::error, where clause is not allowed on pallet error item";
return Err(syn::Error::new(item.generics.where_clause.as_ref().unwrap().span(), msg));
}
let error = syn::parse2::<keyword::Error>(item.ident.to_token_stream())?;
let variants = item
.variants
.iter()
.map(|variant| {
let field_ty = match &variant.fields {
Fields::Unit => None,
Fields::Named(_) => Some(VariantField { is_named: true }),
Fields::Unnamed(_) => Some(VariantField { is_named: false }),
};
match &variant.discriminant {
None |
Some((_, syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Int(_), .. }))) => {},
Some((_, expr)) => {
let msg = "Invalid pallet::error, only integer discriminants are supported";
return Err(syn::Error::new(expr.span(), msg));
},
}
let cfg_attrs: Vec<syn::Attribute> = helper::get_item_cfg_attrs(&variant.attrs);
let maybe_allow_attrs = extract_or_return_allow_attrs(&variant.attrs).collect();
Ok(VariantDef {
ident: variant.ident.clone(),
field: field_ty,
cfg_attrs,
maybe_allow_attrs,
})
})
.collect::<Result<_, _>>()?;
Ok(ErrorDef { attr_span, index, variants, instances, error })
}
}
@@ -0,0 +1,143 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::helper;
use quote::ToTokens;
use syn::spanned::Spanned;
/// List of additional token to be used for parsing.
mod keyword {
syn::custom_keyword!(Event);
syn::custom_keyword!(pallet);
syn::custom_keyword!(generate_deposit);
syn::custom_keyword!(deposit_event);
}
/// Definition for pallet event enum.
pub struct EventDef {
/// The index of event item in pallet module.
pub index: usize,
/// The keyword Event used (contains span).
pub event: keyword::Event,
/// A set of usage of instance, must be check for consistency with trait.
pub instances: Vec<helper::InstanceUsage>,
/// The kind of generic the type `Event` has.
pub gen_kind: super::GenericKind,
/// Whether the function `deposit_event` must be generated.
pub deposit_event: Option<PalletEventDepositAttr>,
/// Where clause used in event definition.
pub where_clause: Option<syn::WhereClause>,
/// The span of the pallet::event attribute.
pub attr_span: proc_macro2::Span,
}
/// Attribute for a pallet's Event.
///
/// Syntax is:
/// * `#[pallet::generate_deposit($vis fn deposit_event)]`
pub struct PalletEventDepositAttr {
pub fn_vis: syn::Visibility,
// Span for the keyword deposit_event
pub fn_span: proc_macro2::Span,
// Span of the attribute
pub span: proc_macro2::Span,
}
impl syn::parse::Parse for PalletEventDepositAttr {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
input.parse::<syn::Token![#]>()?;
let content;
syn::bracketed!(content in input);
content.parse::<keyword::pallet>()?;
content.parse::<syn::Token![::]>()?;
let span = content.parse::<keyword::generate_deposit>()?.span();
let generate_content;
syn::parenthesized!(generate_content in content);
let fn_vis = generate_content.parse::<syn::Visibility>()?;
generate_content.parse::<syn::Token![fn]>()?;
let fn_span = generate_content.parse::<keyword::deposit_event>()?.span();
Ok(PalletEventDepositAttr { fn_vis, span, fn_span })
}
}
struct PalletEventAttrInfo {
deposit_event: Option<PalletEventDepositAttr>,
}
impl PalletEventAttrInfo {
fn from_attrs(attrs: Vec<PalletEventDepositAttr>) -> syn::Result<Self> {
let mut deposit_event = None;
for attr in attrs {
if deposit_event.is_none() {
deposit_event = Some(attr)
} else {
return Err(syn::Error::new(attr.span, "Duplicate attribute"));
}
}
Ok(PalletEventAttrInfo { deposit_event })
}
}
impl EventDef {
pub fn try_from(
attr_span: proc_macro2::Span,
index: usize,
item: &mut syn::Item,
) -> syn::Result<Self> {
let item = if let syn::Item::Enum(item) = item {
item
} else {
return Err(syn::Error::new(item.span(), "Invalid pallet::event, expected enum item"));
};
crate::deprecation::prevent_deprecation_attr_on_outer_enum(&item.attrs)?;
let event_attrs: Vec<PalletEventDepositAttr> =
helper::take_item_pallet_attrs(&mut item.attrs)?;
let attr_info = PalletEventAttrInfo::from_attrs(event_attrs)?;
let deposit_event = attr_info.deposit_event;
if !matches!(item.vis, syn::Visibility::Public(_)) {
let msg = "Invalid pallet::event, `Event` must be public";
return Err(syn::Error::new(item.span(), msg));
}
let where_clause = item.generics.where_clause.clone();
let mut instances = vec![];
// NOTE: Event is not allowed to be only generic on I because it is not supported
// by construct_runtime.
if let Some(u) = helper::check_type_def_optional_gen(&item.generics, item.ident.span())? {
instances.push(u);
} else {
// construct_runtime only allow non generic event for non instantiable pallet.
instances.push(helper::InstanceUsage { has_instance: false, span: item.ident.span() })
}
let has_instance = item.generics.type_params().any(|t| t.ident == "I");
let has_config = item.generics.type_params().any(|t| t.ident == "T");
let gen_kind = super::GenericKind::from_gens(has_config, has_instance)
.expect("Checked by `helper::check_type_def_optional_gen` above");
let event = syn::parse2::<keyword::Event>(item.ident.to_token_stream())?;
Ok(EventDef { attr_span, index, instances, deposit_event, event, gen_kind, where_clause })
}
}
@@ -0,0 +1,156 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::helper;
use pezframe_support_procedural_tools::get_doc_literals;
use syn::spanned::Spanned;
/// List of additional token to be used for parsing.
mod keyword {
syn::custom_keyword!(DispatchResultWithPostInfo);
syn::custom_keyword!(Call);
syn::custom_keyword!(OriginFor);
syn::custom_keyword!(weight);
syn::custom_keyword!(compact);
syn::custom_keyword!(T);
syn::custom_keyword!(pallet);
syn::custom_keyword!(constant_name);
}
/// Definition of extra constants typically `impl<T: Config> Pallet<T> { ... }`
pub struct ExtraConstantsDef {
/// The where_clause used.
pub where_clause: Option<syn::WhereClause>,
/// A set of usage of instance, must be check for consistency with trait.
pub instances: Vec<helper::InstanceUsage>,
/// The extra constant defined.
pub extra_constants: Vec<ExtraConstantDef>,
}
/// Input definition for an constant in pallet.
pub struct ExtraConstantDef {
/// Name of the function
pub ident: syn::Ident,
/// The type returned by the function
pub type_: syn::Type,
/// The doc associated
pub doc: Vec<syn::Expr>,
/// Optional MetaData Name
pub metadata_name: Option<syn::Ident>,
/// Attributes
pub attrs: Vec<syn::Attribute>,
}
/// Attributes for functions in extra_constants impl block.
/// Parse for `#[pallet::constant_name(ConstantName)]`
pub struct ExtraConstAttr {
metadata_name: syn::Ident,
}
impl syn::parse::Parse for ExtraConstAttr {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
input.parse::<syn::Token![#]>()?;
let content;
syn::bracketed!(content in input);
content.parse::<keyword::pallet>()?;
content.parse::<syn::Token![::]>()?;
content.parse::<keyword::constant_name>()?;
let metadata_name;
syn::parenthesized!(metadata_name in content);
Ok(ExtraConstAttr { metadata_name: metadata_name.parse::<syn::Ident>()? })
}
}
impl ExtraConstantsDef {
pub fn try_from(item: &mut syn::Item) -> syn::Result<Self> {
let item = if let syn::Item::Impl(item) = item {
item
} else {
return Err(syn::Error::new(
item.span(),
"Invalid pallet::extra_constants, expected item impl",
));
};
let instances = vec![
helper::check_impl_gen(&item.generics, item.impl_token.span())?,
helper::check_pallet_struct_usage(&item.self_ty)?,
];
if let Some((_, _, for_)) = item.trait_ {
let msg = "Invalid pallet::call, expected no trait ident as in \
`impl<..> Pallet<..> { .. }`";
return Err(syn::Error::new(for_.span(), msg));
}
let mut extra_constants = vec![];
for impl_item in &mut item.items {
let method = if let syn::ImplItem::Fn(method) = impl_item {
method
} else {
let msg = "Invalid pallet::call, only method accepted";
return Err(syn::Error::new(impl_item.span(), msg));
};
if !method.sig.inputs.is_empty() {
let msg = "Invalid pallet::extra_constants, method must have 0 args";
return Err(syn::Error::new(method.sig.span(), msg));
}
if !method.sig.generics.params.is_empty() {
let msg = "Invalid pallet::extra_constants, method must have 0 generics";
return Err(syn::Error::new(method.sig.generics.params[0].span(), msg));
}
if method.sig.generics.where_clause.is_some() {
let msg = "Invalid pallet::extra_constants, method must have no where clause";
return Err(syn::Error::new(method.sig.generics.where_clause.span(), msg));
}
let type_ = match &method.sig.output {
syn::ReturnType::Default => {
let msg = "Invalid pallet::extra_constants, method must have a return type";
return Err(syn::Error::new(method.span(), msg));
},
syn::ReturnType::Type(_, type_) => *type_.clone(),
};
// parse metadata_name
let mut extra_constant_attrs: Vec<ExtraConstAttr> =
helper::take_item_pallet_attrs(method)?;
if extra_constant_attrs.len() > 1 {
let msg =
"Invalid attribute in pallet::constant_name, only one attribute is expected";
return Err(syn::Error::new(extra_constant_attrs[1].metadata_name.span(), msg));
}
let metadata_name = extra_constant_attrs.pop().map(|attr| attr.metadata_name);
extra_constants.push(ExtraConstantDef {
ident: method.sig.ident.clone(),
type_,
doc: get_doc_literals(&method.attrs),
metadata_name,
attrs: method.attrs.clone(),
});
}
Ok(Self { instances, where_clause: item.generics.where_clause.clone(), extra_constants })
}
}
@@ -0,0 +1,55 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::helper;
use syn::spanned::Spanned;
/// Definition for pallet genesis build implementation.
pub struct GenesisBuildDef {
/// A set of usage of instance, must be check for consistency with trait.
pub instances: Option<Vec<helper::InstanceUsage>>,
/// The where_clause used.
pub where_clause: Option<syn::WhereClause>,
/// The span of the pallet::genesis_build attribute.
pub attr_span: proc_macro2::Span,
}
impl GenesisBuildDef {
pub fn try_from(attr_span: proc_macro2::Span, item: &mut syn::Item) -> syn::Result<Self> {
let item = if let syn::Item::Impl(item) = item {
item
} else {
let msg = "Invalid pallet::genesis_build, expected item impl";
return Err(syn::Error::new(item.span(), msg));
};
let item_trait = &item
.trait_
.as_ref()
.ok_or_else(|| {
let msg = "Invalid pallet::genesis_build, expected impl<..> GenesisBuild<..> \
for GenesisConfig<..>";
syn::Error::new(item.span(), msg)
})?
.1;
let instances =
helper::check_genesis_builder_usage(item_trait)?.map(|instances| vec![instances]);
Ok(Self { attr_span, instances, where_clause: item.generics.where_clause.clone() })
}
}
@@ -0,0 +1,73 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::helper;
use syn::spanned::Spanned;
/// Definition for pallet genesis config type.
///
/// Either:
/// * `struct GenesisConfig`
/// * `enum GenesisConfig`
pub struct GenesisConfigDef {
/// The index of item in pallet module.
pub index: usize,
/// The kind of generic the type `GenesisConfig` has.
pub gen_kind: super::GenericKind,
/// A set of usage of instance, must be check for consistency with trait.
pub instances: Vec<helper::InstanceUsage>,
/// The ident of genesis_config, can be used for span.
pub genesis_config: syn::Ident,
}
impl GenesisConfigDef {
pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result<Self> {
let item_span = item.span();
let (vis, ident, generics) = match &item {
syn::Item::Enum(item) => (&item.vis, &item.ident, &item.generics),
syn::Item::Struct(item) => (&item.vis, &item.ident, &item.generics),
_ => {
let msg = "Invalid pallet::genesis_config, expected enum or struct";
return Err(syn::Error::new(item.span(), msg));
},
};
let mut instances = vec![];
// NOTE: GenesisConfig is not allowed to be only generic on I because it is not supported
// by construct_runtime.
if let Some(u) = helper::check_type_def_optional_gen(generics, ident.span())? {
instances.push(u);
}
let has_instance = generics.type_params().any(|t| t.ident == "I");
let has_config = generics.type_params().any(|t| t.ident == "T");
let gen_kind = super::GenericKind::from_gens(has_config, has_instance)
.expect("Checked by `helper::check_type_def_optional_gen` above");
if !matches!(vis, syn::Visibility::Public(_)) {
let msg = "Invalid pallet::genesis_config, GenesisConfig must be public";
return Err(syn::Error::new(item_span, msg));
}
if ident != "GenesisConfig" {
let msg = "Invalid pallet::genesis_config, ident must `GenesisConfig`";
return Err(syn::Error::new(ident.span(), msg));
}
Ok(GenesisConfigDef { index, genesis_config: ident.clone(), instances, gen_kind })
}
}
@@ -0,0 +1,645 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::spanned::Spanned;
/// List of additional token to be used for parsing.
mod keyword {
syn::custom_keyword!(I);
syn::custom_keyword!(compact);
syn::custom_keyword!(GenesisBuild);
syn::custom_keyword!(BuildGenesisConfig);
syn::custom_keyword!(Config);
syn::custom_keyword!(T);
syn::custom_keyword!(Pallet);
syn::custom_keyword!(origin);
syn::custom_keyword!(DispatchResult);
syn::custom_keyword!(DispatchResultWithPostInfo);
}
/// A usage of instance, either the trait `Config` has been used with instance or without instance.
/// Used to check for consistency.
#[derive(Clone)]
pub struct InstanceUsage {
pub has_instance: bool,
pub span: proc_macro2::Span,
}
/// Trait implemented for syn items to get mutable references on their attributes.
///
/// NOTE: verbatim variants are not supported.
pub trait MutItemAttrs {
fn mut_item_attrs(&mut self) -> Option<&mut Vec<syn::Attribute>>;
}
/// Take the first pallet attribute (e.g. attribute like `#[pallet..]`) and decode it to `Attr`
pub(crate) fn take_first_item_pallet_attr<Attr>(
item: &mut impl MutItemAttrs,
) -> syn::Result<Option<Attr>>
where
Attr: syn::parse::Parse,
{
let Some(attrs) = item.mut_item_attrs() else { return Ok(None) };
let Some(index) = attrs.iter().position(|attr| {
attr.path().segments.first().map_or(false, |segment| segment.ident == "pallet")
}) else {
return Ok(None);
};
let pezpallet_attr = attrs.remove(index);
Ok(Some(syn::parse2(pezpallet_attr.into_token_stream())?))
}
/// Take all the pallet attributes (e.g. attribute like `#[pallet..]`) and decode them to `Attr`
pub(crate) fn take_item_pallet_attrs<Attr>(item: &mut impl MutItemAttrs) -> syn::Result<Vec<Attr>>
where
Attr: syn::parse::Parse,
{
let mut pezpallet_attrs = Vec::new();
while let Some(attr) = take_first_item_pallet_attr(item)? {
pezpallet_attrs.push(attr)
}
Ok(pezpallet_attrs)
}
/// Get all the cfg attributes (e.g. attribute like `#[cfg..]`) and decode them to `Attr`
pub fn get_item_cfg_attrs(attrs: &[syn::Attribute]) -> Vec<syn::Attribute> {
attrs
.iter()
.filter_map(|attr| {
if attr.path().segments.first().map_or(false, |segment| segment.ident == "cfg") {
Some(attr.clone())
} else {
None
}
})
.collect::<Vec<_>>()
}
impl MutItemAttrs for syn::Item {
fn mut_item_attrs(&mut self) -> Option<&mut Vec<syn::Attribute>> {
match self {
Self::Const(item) => Some(item.attrs.as_mut()),
Self::Enum(item) => Some(item.attrs.as_mut()),
Self::ExternCrate(item) => Some(item.attrs.as_mut()),
Self::Fn(item) => Some(item.attrs.as_mut()),
Self::ForeignMod(item) => Some(item.attrs.as_mut()),
Self::Impl(item) => Some(item.attrs.as_mut()),
Self::Macro(item) => Some(item.attrs.as_mut()),
Self::Mod(item) => Some(item.attrs.as_mut()),
Self::Static(item) => Some(item.attrs.as_mut()),
Self::Struct(item) => Some(item.attrs.as_mut()),
Self::Trait(item) => Some(item.attrs.as_mut()),
Self::TraitAlias(item) => Some(item.attrs.as_mut()),
Self::Type(item) => Some(item.attrs.as_mut()),
Self::Union(item) => Some(item.attrs.as_mut()),
Self::Use(item) => Some(item.attrs.as_mut()),
_ => None,
}
}
}
impl MutItemAttrs for syn::TraitItem {
fn mut_item_attrs(&mut self) -> Option<&mut Vec<syn::Attribute>> {
match self {
Self::Const(item) => Some(item.attrs.as_mut()),
Self::Fn(item) => Some(item.attrs.as_mut()),
Self::Type(item) => Some(item.attrs.as_mut()),
Self::Macro(item) => Some(item.attrs.as_mut()),
_ => None,
}
}
}
impl MutItemAttrs for Vec<syn::Attribute> {
fn mut_item_attrs(&mut self) -> Option<&mut Vec<syn::Attribute>> {
Some(self)
}
}
impl MutItemAttrs for syn::ItemMod {
fn mut_item_attrs(&mut self) -> Option<&mut Vec<syn::Attribute>> {
Some(&mut self.attrs)
}
}
impl MutItemAttrs for syn::ImplItemFn {
fn mut_item_attrs(&mut self) -> Option<&mut Vec<syn::Attribute>> {
Some(&mut self.attrs)
}
}
impl MutItemAttrs for syn::ItemType {
fn mut_item_attrs(&mut self) -> Option<&mut Vec<syn::Attribute>> {
Some(&mut self.attrs)
}
}
/// Parse for `()`
struct Unit;
impl syn::parse::Parse for Unit {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let content;
syn::parenthesized!(content in input);
if !content.is_empty() {
let msg = "unexpected tokens, expected nothing inside parenthesis as `()`";
return Err(syn::Error::new(content.span(), msg));
}
Ok(Self)
}
}
/// Parse for `'static`
struct StaticLifetime;
impl syn::parse::Parse for StaticLifetime {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let lifetime = input.parse::<syn::Lifetime>()?;
if lifetime.ident != "static" {
let msg = "unexpected tokens, expected `static`";
return Err(syn::Error::new(lifetime.ident.span(), msg));
}
Ok(Self)
}
}
/// Check the syntax: `I: 'static = ()`
///
/// `span` is used in case generics is empty (empty generics has span == call_site).
///
/// return the instance if found.
pub fn check_config_def_gen(gen: &syn::Generics, span: proc_macro2::Span) -> syn::Result<()> {
let expected = "expected `I: 'static = ()`";
pub struct CheckTraitDefGenerics;
impl syn::parse::Parse for CheckTraitDefGenerics {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
input.parse::<keyword::I>()?;
input.parse::<syn::Token![:]>()?;
input.parse::<StaticLifetime>()?;
input.parse::<syn::Token![=]>()?;
input.parse::<Unit>()?;
Ok(Self)
}
}
syn::parse2::<CheckTraitDefGenerics>(gen.params.to_token_stream()).map_err(|e| {
let msg = format!("Invalid generics: {}", expected);
let mut err = syn::Error::new(span, msg);
err.combine(e);
err
})?;
Ok(())
}
/// Check the syntax:
/// * either `T`
/// * or `T, I = ()`
///
/// `span` is used in case generics is empty (empty generics has span == call_site).
///
/// return the instance if found.
pub fn check_type_def_gen_no_bounds(
gen: &syn::Generics,
span: proc_macro2::Span,
) -> syn::Result<InstanceUsage> {
let expected = "expected `T` or `T, I = ()`";
pub struct Checker(InstanceUsage);
impl syn::parse::Parse for Checker {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut instance_usage = InstanceUsage { has_instance: false, span: input.span() };
input.parse::<keyword::T>()?;
if input.peek(syn::Token![,]) {
instance_usage.has_instance = true;
input.parse::<syn::Token![,]>()?;
input.parse::<keyword::I>()?;
input.parse::<syn::Token![=]>()?;
input.parse::<Unit>()?;
}
Ok(Self(instance_usage))
}
}
let i = syn::parse2::<Checker>(gen.params.to_token_stream())
.map_err(|e| {
let msg = format!("Invalid type def generics: {}", expected);
let mut err = syn::Error::new(span, msg);
err.combine(e);
err
})?
.0;
Ok(i)
}
/// Check the syntax:
/// * either `` (no generics
/// * or `T`
/// * or `T: Config`
/// * or `T, I = ()`
/// * or `T: Config<I>, I: 'static = ()`
///
/// `span` is used in case generics is empty (empty generics has span == call_site).
///
/// return some instance usage if there is some generic, or none otherwise.
pub fn check_type_def_optional_gen(
gen: &syn::Generics,
span: proc_macro2::Span,
) -> syn::Result<Option<InstanceUsage>> {
let expected = "expected `` or `T` or `T: Config` or `T, I = ()` or \
`T: Config<I>, I: 'static = ()`";
pub struct Checker(Option<InstanceUsage>);
impl syn::parse::Parse for Checker {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
if input.is_empty() {
return Ok(Self(None));
}
let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false };
input.parse::<keyword::T>()?;
if input.is_empty() {
return Ok(Self(Some(instance_usage)));
}
let lookahead = input.lookahead1();
if lookahead.peek(syn::Token![,]) {
instance_usage.has_instance = true;
input.parse::<syn::Token![,]>()?;
input.parse::<keyword::I>()?;
input.parse::<syn::Token![=]>()?;
input.parse::<Unit>()?;
Ok(Self(Some(instance_usage)))
} else if lookahead.peek(syn::Token![:]) {
input.parse::<syn::Token![:]>()?;
input.parse::<keyword::Config>()?;
if input.is_empty() {
return Ok(Self(Some(instance_usage)));
}
instance_usage.has_instance = true;
input.parse::<syn::Token![<]>()?;
input.parse::<keyword::I>()?;
input.parse::<syn::Token![>]>()?;
input.parse::<syn::Token![,]>()?;
input.parse::<keyword::I>()?;
input.parse::<syn::Token![:]>()?;
input.parse::<StaticLifetime>()?;
input.parse::<syn::Token![=]>()?;
input.parse::<Unit>()?;
Ok(Self(Some(instance_usage)))
} else {
Err(lookahead.error())
}
}
}
let i = syn::parse2::<Checker>(gen.params.to_token_stream())
.map_err(|e| {
let msg = format!("Invalid type def generics: {}", expected);
let mut err = syn::Error::new(span, msg);
err.combine(e);
err
})?
.0
// Span can be call_site if generic is empty. Thus we replace it.
.map(|mut i| {
i.span = span;
i
});
Ok(i)
}
/// Check the syntax:
/// * either `Pallet<T>`
/// * or `Pallet<T, I>`
///
/// return the instance if found.
pub fn check_pallet_struct_usage(type_: &Box<syn::Type>) -> syn::Result<InstanceUsage> {
let expected = "expected `Pallet<T>` or `Pallet<T, I>`";
pub struct Checker(InstanceUsage);
impl syn::parse::Parse for Checker {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false };
input.parse::<keyword::Pallet>()?;
input.parse::<syn::Token![<]>()?;
input.parse::<keyword::T>()?;
if input.peek(syn::Token![,]) {
instance_usage.has_instance = true;
input.parse::<syn::Token![,]>()?;
input.parse::<keyword::I>()?;
}
input.parse::<syn::Token![>]>()?;
Ok(Self(instance_usage))
}
}
let i = syn::parse2::<Checker>(type_.to_token_stream())
.map_err(|e| {
let msg = format!("Invalid pallet struct: {}", expected);
let mut err = syn::Error::new(type_.span(), msg);
err.combine(e);
err
})?
.0;
Ok(i)
}
/// Check the generic is:
/// * either `T: Config`
/// * or `T: Config<I>, I: 'static`
///
/// `span` is used in case generics is empty (empty generics has span == call_site).
///
/// return whether it contains instance.
pub fn check_impl_gen(gen: &syn::Generics, span: proc_macro2::Span) -> syn::Result<InstanceUsage> {
let expected = "expected `impl<T: Config>` or `impl<T: Config<I>, I: 'static>`";
pub struct Checker(InstanceUsage);
impl syn::parse::Parse for Checker {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false };
input.parse::<keyword::T>()?;
input.parse::<syn::Token![:]>()?;
input.parse::<keyword::Config>()?;
if input.peek(syn::Token![<]) {
instance_usage.has_instance = true;
input.parse::<syn::Token![<]>()?;
input.parse::<keyword::I>()?;
input.parse::<syn::Token![>]>()?;
input.parse::<syn::Token![,]>()?;
input.parse::<keyword::I>()?;
input.parse::<syn::Token![:]>()?;
input.parse::<StaticLifetime>()?;
}
Ok(Self(instance_usage))
}
}
let i = syn::parse2::<Checker>(gen.params.to_token_stream())
.map_err(|e| {
let mut err = syn::Error::new(span, format!("Invalid generics: {}", expected));
err.combine(e);
err
})?
.0;
Ok(i)
}
/// Check the syntax:
/// * or `T`
/// * or `T: Config`
/// * or `T, I = ()`
/// * or `T: Config<I>, I: 'static = ()`
///
/// `span` is used in case generics is empty (empty generics has span == call_site).
///
/// return the instance if found.
pub fn check_type_def_gen(
gen: &syn::Generics,
span: proc_macro2::Span,
) -> syn::Result<InstanceUsage> {
let expected = "expected `T` or `T: Config` or `T, I = ()` or \
`T: Config<I>, I: 'static = ()`";
pub struct Checker(InstanceUsage);
impl syn::parse::Parse for Checker {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false };
input.parse::<keyword::T>()?;
if input.is_empty() {
return Ok(Self(instance_usage));
}
let lookahead = input.lookahead1();
if lookahead.peek(syn::Token![,]) {
instance_usage.has_instance = true;
input.parse::<syn::Token![,]>()?;
input.parse::<keyword::I>()?;
input.parse::<syn::Token![=]>()?;
input.parse::<Unit>()?;
Ok(Self(instance_usage))
} else if lookahead.peek(syn::Token![:]) {
input.parse::<syn::Token![:]>()?;
input.parse::<keyword::Config>()?;
if input.is_empty() {
return Ok(Self(instance_usage));
}
instance_usage.has_instance = true;
input.parse::<syn::Token![<]>()?;
input.parse::<keyword::I>()?;
input.parse::<syn::Token![>]>()?;
input.parse::<syn::Token![,]>()?;
input.parse::<keyword::I>()?;
input.parse::<syn::Token![:]>()?;
input.parse::<StaticLifetime>()?;
input.parse::<syn::Token![=]>()?;
input.parse::<Unit>()?;
Ok(Self(instance_usage))
} else {
Err(lookahead.error())
}
}
}
let mut i = syn::parse2::<Checker>(gen.params.to_token_stream())
.map_err(|e| {
let msg = format!("Invalid type def generics: {}", expected);
let mut err = syn::Error::new(span, msg);
err.combine(e);
err
})?
.0;
// Span can be call_site if generic is empty. Thus we replace it.
i.span = span;
Ok(i)
}
/// Check the syntax:
/// * either `GenesisBuild<T>`
/// * or `GenesisBuild<T, I>`
/// * or `BuildGenesisConfig`
///
/// return the instance if found for `GenesisBuild`
/// return None for BuildGenesisConfig
pub fn check_genesis_builder_usage(type_: &syn::Path) -> syn::Result<Option<InstanceUsage>> {
let expected = "expected `BuildGenesisConfig` (or the deprecated `GenesisBuild<T>` or `GenesisBuild<T, I>`)";
pub struct Checker(Option<InstanceUsage>);
impl syn::parse::Parse for Checker {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false };
if input.peek(keyword::GenesisBuild) {
input.parse::<keyword::GenesisBuild>()?;
input.parse::<syn::Token![<]>()?;
input.parse::<keyword::T>()?;
if input.peek(syn::Token![,]) {
instance_usage.has_instance = true;
input.parse::<syn::Token![,]>()?;
input.parse::<keyword::I>()?;
}
input.parse::<syn::Token![>]>()?;
return Ok(Self(Some(instance_usage)));
} else {
input.parse::<keyword::BuildGenesisConfig>()?;
return Ok(Self(None));
}
}
}
let i = syn::parse2::<Checker>(type_.to_token_stream())
.map_err(|e| {
let msg = format!("Invalid genesis builder: {}", expected);
let mut err = syn::Error::new(type_.span(), msg);
err.combine(e);
err
})?
.0;
Ok(i)
}
/// Check the syntax:
/// * either `` (no generics)
/// * or `T: Config`
/// * or `T: Config<I>, I: 'static`
///
/// `span` is used in case generics is empty (empty generics has span == call_site).
///
/// return the instance if found.
pub fn check_type_value_gen(
gen: &syn::Generics,
span: proc_macro2::Span,
) -> syn::Result<Option<InstanceUsage>> {
let expected = "expected `` or `T: Config` or `T: Config<I>, I: 'static`";
pub struct Checker(Option<InstanceUsage>);
impl syn::parse::Parse for Checker {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
if input.is_empty() {
return Ok(Self(None));
}
input.parse::<keyword::T>()?;
input.parse::<syn::Token![:]>()?;
input.parse::<keyword::Config>()?;
let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false };
if input.is_empty() {
return Ok(Self(Some(instance_usage)));
}
instance_usage.has_instance = true;
input.parse::<syn::Token![<]>()?;
input.parse::<keyword::I>()?;
input.parse::<syn::Token![>]>()?;
input.parse::<syn::Token![,]>()?;
input.parse::<keyword::I>()?;
input.parse::<syn::Token![:]>()?;
input.parse::<StaticLifetime>()?;
Ok(Self(Some(instance_usage)))
}
}
let i = syn::parse2::<Checker>(gen.params.to_token_stream())
.map_err(|e| {
let msg = format!("Invalid type def generics: {}", expected);
let mut err = syn::Error::new(span, msg);
err.combine(e);
err
})?
.0
// Span can be call_site if generic is empty. Thus we replace it.
.map(|mut i| {
i.span = span;
i
});
Ok(i)
}
/// The possible return type of a dispatchable.
#[derive(Clone)]
pub enum CallReturnType {
DispatchResult,
DispatchResultWithPostInfo,
}
/// Check the keyword `DispatchResultWithPostInfo` or `DispatchResult`.
pub fn check_pallet_call_return_type(sig: &syn::Signature) -> syn::Result<CallReturnType> {
let syn::ReturnType::Type(_, type_) = &sig.output else {
let msg = "Invalid pallet::call, require return type \
DispatchResultWithPostInfo";
return Err(syn::Error::new(sig.span(), msg));
};
pub struct Checker(CallReturnType);
impl syn::parse::Parse for Checker {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(keyword::DispatchResultWithPostInfo) {
input.parse::<keyword::DispatchResultWithPostInfo>()?;
Ok(Self(CallReturnType::DispatchResultWithPostInfo))
} else if lookahead.peek(keyword::DispatchResult) {
input.parse::<keyword::DispatchResult>()?;
Ok(Self(CallReturnType::DispatchResult))
} else {
Err(lookahead.error())
}
}
}
syn::parse2::<Checker>(type_.to_token_stream()).map(|c| c.0)
}
pub(crate) fn two128_str(s: &str) -> TokenStream {
bytes_to_array(pezsp_crypto_hashing::twox_128(s.as_bytes()).into_iter())
}
pub(crate) fn bytes_to_array(bytes: impl IntoIterator<Item = u8>) -> TokenStream {
let bytes = bytes.into_iter();
quote!(
[ #( #bytes ),* ]
)
.into()
}
@@ -0,0 +1,79 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::helper;
use syn::spanned::Spanned;
/// Implementation of the pallet hooks.
pub struct HooksDef {
/// A set of usage of instance, must be check for consistency with trait.
pub instances: Vec<helper::InstanceUsage>,
/// The where_clause used.
pub where_clause: Option<syn::WhereClause>,
/// The span of the pallet::hooks attribute.
pub attr_span: proc_macro2::Span,
/// Boolean flag, set to true if the `on_runtime_upgrade` method of hooks was implemented.
pub has_runtime_upgrade: bool,
}
impl HooksDef {
pub fn try_from(attr_span: proc_macro2::Span, item: &mut syn::Item) -> syn::Result<Self> {
let item = if let syn::Item::Impl(item) = item {
item
} else {
let msg = "Invalid pallet::hooks, expected item impl";
return Err(syn::Error::new(item.span(), msg));
};
let instances = vec![
helper::check_pallet_struct_usage(&item.self_ty)?,
helper::check_impl_gen(&item.generics, item.impl_token.span())?,
];
let item_trait = &item
.trait_
.as_ref()
.ok_or_else(|| {
let msg = "Invalid pallet::hooks, expected impl<..> Hooks \
for Pallet<..>";
syn::Error::new(item.span(), msg)
})?
.1;
if item_trait.segments.len() != 1 || item_trait.segments[0].ident != "Hooks" {
let msg = format!(
"Invalid pallet::hooks, expected trait to be `Hooks` found `{}`\
, you can import from `pezframe_support::pezpallet_prelude`",
quote::quote!(#item_trait)
);
return Err(syn::Error::new(item_trait.span(), msg));
}
let has_runtime_upgrade = item.items.iter().any(|i| match i {
syn::ImplItem::Fn(method) => method.sig.ident == "on_runtime_upgrade",
_ => false,
});
Ok(Self {
attr_span,
instances,
has_runtime_upgrade,
where_clause: item.generics.where_clause.clone(),
})
}
}
@@ -0,0 +1,58 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::helper;
use syn::spanned::Spanned;
/// The definition of the pallet inherent implementation.
pub struct InherentDef {
/// A set of usage of instance, must be check for consistency with trait.
pub instances: Vec<helper::InstanceUsage>,
}
impl InherentDef {
pub fn try_from(item: &mut syn::Item) -> syn::Result<Self> {
let item = if let syn::Item::Impl(item) = item {
item
} else {
let msg = "Invalid pallet::inherent, expected item impl";
return Err(syn::Error::new(item.span(), msg));
};
if item.trait_.is_none() {
let msg = "Invalid pallet::inherent, expected impl<..> ProvideInherent for Pallet<..>";
return Err(syn::Error::new(item.span(), msg));
}
if let Some(last) = item.trait_.as_ref().unwrap().1.segments.last() {
if last.ident != "ProvideInherent" {
let msg = "Invalid pallet::inherent, expected trait ProvideInherent";
return Err(syn::Error::new(last.span(), msg));
}
} else {
let msg = "Invalid pallet::inherent, expected impl<..> ProvideInherent for Pallet<..>";
return Err(syn::Error::new(item.span(), msg));
}
let instances = vec![
helper::check_pallet_struct_usage(&item.self_ty)?,
helper::check_impl_gen(&item.generics, item.impl_token.span())?,
];
Ok(InherentDef { instances })
}
}
@@ -0,0 +1,833 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Parse for pallet macro.
//!
//! Parse the module into `Def` struct through `Def::try_from` function.
pub mod call;
pub mod composite;
pub mod config;
pub mod error;
pub mod event;
pub mod extra_constants;
pub mod genesis_build;
pub mod genesis_config;
pub mod helper;
pub mod hooks;
pub mod inherent;
pub mod origin;
pub mod pezpallet_struct;
pub mod storage;
pub mod tasks;
pub mod type_value;
pub mod validate_unsigned;
pub mod view_functions;
#[cfg(test)]
pub mod tests;
use composite::{keyword::CompositeKeyword, CompositeDef};
use pezframe_support_procedural_tools::generate_access_from_frame_or_crate;
use quote::ToTokens;
use syn::spanned::Spanned;
/// Parsed definition of a pallet.
pub struct Def {
/// The module items.
/// (their order must not be modified because they are registered in individual definitions).
pub item: syn::ItemMod,
pub config: config::ConfigDef,
pub pezpallet_struct: pezpallet_struct::PalletStructDef,
pub hooks: Option<hooks::HooksDef>,
pub call: Option<call::CallDef>,
pub tasks: Option<tasks::TasksDef>,
pub task_enum: Option<tasks::TaskEnumDef>,
pub storages: Vec<storage::StorageDef>,
pub error: Option<error::ErrorDef>,
pub event: Option<event::EventDef>,
pub origin: Option<origin::OriginDef>,
pub inherent: Option<inherent::InherentDef>,
pub genesis_config: Option<genesis_config::GenesisConfigDef>,
pub genesis_build: Option<genesis_build::GenesisBuildDef>,
pub validate_unsigned: Option<validate_unsigned::ValidateUnsignedDef>,
pub extra_constants: Option<extra_constants::ExtraConstantsDef>,
pub composites: Vec<composite::CompositeDef>,
pub type_values: Vec<type_value::TypeValueDef>,
pub pezframe_system: syn::Path,
pub pezframe_support: syn::Path,
pub dev_mode: bool,
pub view_functions: Option<view_functions::ViewFunctionsImplDef>,
pub is_pezframe_system: bool,
}
impl Def {
pub fn try_from(mut item: syn::ItemMod, dev_mode: bool) -> syn::Result<Self> {
let pezframe_system = generate_access_from_frame_or_crate("pezframe-system")?;
let pezframe_support = generate_access_from_frame_or_crate("pezframe-support")?;
let item_span = item.span();
let items = &mut item
.content
.as_mut()
.ok_or_else(|| {
let msg = "Invalid pallet definition, expected mod to be inlined.";
syn::Error::new(item_span, msg)
})?
.1;
let mut config = None;
let mut pezpallet_struct = None;
let mut hooks = None;
let mut call = None;
let mut tasks = None;
let mut task_enum = None;
let mut error = None;
let mut event = None;
let mut origin = None;
let mut inherent = None;
let mut genesis_config = None;
let mut genesis_build = None;
let mut validate_unsigned = None;
let mut extra_constants = None;
let mut storages = vec![];
let mut type_values = vec![];
let mut composites: Vec<CompositeDef> = vec![];
let mut view_functions = None;
let mut is_pezframe_system = false;
for (index, item) in items.iter_mut().enumerate() {
let pezpallet_attr: Option<PalletAttr> = helper::take_first_item_pallet_attr(item)?;
match pezpallet_attr {
Some(PalletAttr::Config{ with_default, pezframe_system_config: is_pezframe_system_val, without_automatic_metadata, ..}) if config.is_none() => {
is_pezframe_system = is_pezframe_system_val;
config = Some(config::ConfigDef::try_from(
&pezframe_system,
index,
item,
with_default,
without_automatic_metadata,
is_pezframe_system,
)?);
},
Some(PalletAttr::Pallet(span)) if pezpallet_struct.is_none() => {
let p = pezpallet_struct::PalletStructDef::try_from(span, index, item)?;
pezpallet_struct = Some(p);
},
Some(PalletAttr::Hooks(span)) if hooks.is_none() => {
let m = hooks::HooksDef::try_from(span, item)?;
hooks = Some(m);
},
Some(PalletAttr::RuntimeCall(cw, span)) if call.is_none() =>
call = Some(call::CallDef::try_from(span, index, item, dev_mode, cw)?),
Some(PalletAttr::Tasks(span)) if tasks.is_none() => {
let item_tokens = item.to_token_stream();
// `TasksDef::parse` needs to know if attr was provided so we artificially
// re-insert it here
tasks = Some(syn::parse2::<tasks::TasksDef>(quote::quote_spanned! { span =>
#[pallet::tasks_experimental]
#item_tokens
})?);
// replace item with a no-op because it will be handled by the expansion of tasks
*item = syn::Item::Verbatim(quote::quote!());
}
Some(PalletAttr::TaskCondition(span)) => return Err(syn::Error::new(
span,
"`#[pallet::task_condition]` can only be used on items within an `impl` statement."
)),
Some(PalletAttr::TaskIndex(span)) => return Err(syn::Error::new(
span,
"`#[pallet::task_index]` can only be used on items within an `impl` statement."
)),
Some(PalletAttr::TaskList(span)) => return Err(syn::Error::new(
span,
"`#[pallet::task_list]` can only be used on items within an `impl` statement."
)),
Some(PalletAttr::RuntimeTask(_)) if task_enum.is_none() =>
task_enum = Some(syn::parse2::<tasks::TaskEnumDef>(item.to_token_stream())?),
Some(PalletAttr::Error(span)) if error.is_none() =>
error = Some(error::ErrorDef::try_from(span, index, item)?),
Some(PalletAttr::RuntimeEvent(span)) if event.is_none() =>
event = Some(event::EventDef::try_from(span, index, item)?),
Some(PalletAttr::GenesisConfig(_)) if genesis_config.is_none() => {
let g = genesis_config::GenesisConfigDef::try_from(index, item)?;
genesis_config = Some(g);
},
Some(PalletAttr::GenesisBuild(span)) if genesis_build.is_none() => {
let g = genesis_build::GenesisBuildDef::try_from(span, item)?;
genesis_build = Some(g);
},
Some(PalletAttr::RuntimeOrigin(_)) if origin.is_none() =>
origin = Some(origin::OriginDef::try_from(item)?),
Some(PalletAttr::Inherent(_)) if inherent.is_none() =>
inherent = Some(inherent::InherentDef::try_from(item)?),
Some(PalletAttr::Storage(span)) =>
storages.push(storage::StorageDef::try_from(span, index, item, dev_mode)?),
Some(PalletAttr::ValidateUnsigned(_)) if validate_unsigned.is_none() => {
let v = validate_unsigned::ValidateUnsignedDef::try_from(item)?;
validate_unsigned = Some(v);
},
Some(PalletAttr::TypeValue(span)) =>
type_values.push(type_value::TypeValueDef::try_from(span, index, item)?),
Some(PalletAttr::ExtraConstants(_)) =>
extra_constants =
Some(extra_constants::ExtraConstantsDef::try_from(item)?),
Some(PalletAttr::Composite(span)) => {
let composite =
composite::CompositeDef::try_from(span, &pezframe_support, item)?;
if composites.iter().any(|def| {
match (&def.composite_keyword, &composite.composite_keyword) {
(
CompositeKeyword::FreezeReason(_),
CompositeKeyword::FreezeReason(_),
) |
(CompositeKeyword::HoldReason(_), CompositeKeyword::HoldReason(_)) |
(CompositeKeyword::LockId(_), CompositeKeyword::LockId(_)) |
(
CompositeKeyword::SlashReason(_),
CompositeKeyword::SlashReason(_),
) => true,
_ => false,
}
}) {
let msg = format!(
"Invalid duplicated `{}` definition",
composite.composite_keyword
);
return Err(syn::Error::new(composite.composite_keyword.span(), &msg))
}
composites.push(composite);
},
Some(PalletAttr::ViewFunctions(span)) => {
view_functions = Some(view_functions::ViewFunctionsImplDef::try_from(span, item)?);
}
Some(attr) => {
let msg = "Invalid duplicated attribute";
return Err(syn::Error::new(attr.span(), msg))
},
None => (),
}
}
if genesis_config.is_some() != genesis_build.is_some() {
let msg = format!(
"`#[pallet::genesis_config]` and `#[pallet::genesis_build]` attributes must be \
either both used or both not used, instead genesis_config is {} and genesis_build \
is {}",
genesis_config.as_ref().map_or("unused", |_| "used"),
genesis_build.as_ref().map_or("unused", |_| "used"),
);
return Err(syn::Error::new(item_span, msg));
}
Self::resolve_tasks(&item_span, &mut tasks, &mut task_enum, items)?;
let def = Def {
item,
config: config
.ok_or_else(|| syn::Error::new(item_span, "Missing `#[pallet::config]`"))?,
pezpallet_struct: pezpallet_struct
.ok_or_else(|| syn::Error::new(item_span, "Missing `#[pallet::pallet]`"))?,
hooks,
call,
tasks,
task_enum,
extra_constants,
genesis_config,
genesis_build,
validate_unsigned,
error,
event,
origin,
inherent,
storages,
composites,
type_values,
pezframe_system,
pezframe_support,
dev_mode,
view_functions,
is_pezframe_system,
};
def.check_instance_usage()?;
Ok(def)
}
/// Performs extra logic checks necessary for the `#[pallet::tasks_experimental]` feature.
fn resolve_tasks(
item_span: &proc_macro2::Span,
tasks: &mut Option<tasks::TasksDef>,
task_enum: &mut Option<tasks::TaskEnumDef>,
items: &mut Vec<syn::Item>,
) -> syn::Result<()> {
// fallback for manual (without macros) definition of tasks impl
Self::resolve_manual_tasks_impl(tasks, task_enum, items)?;
// fallback for manual (without macros) definition of task enum
Self::resolve_manual_task_enum(tasks, task_enum, items)?;
// ensure that if `task_enum` is specified, `tasks` is also specified
match (&task_enum, &tasks) {
(Some(_), None) =>
return Err(syn::Error::new(
*item_span,
"Missing `#[pallet::tasks_experimental]` impl",
)),
(None, Some(tasks)) =>
if tasks.tasks_attr.is_none() {
return Err(syn::Error::new(
tasks.item_impl.impl_token.span(),
"A `#[pallet::tasks_experimental]` attribute must be attached to your `Task` impl if the \
task enum has been omitted",
));
} else {
},
_ => (),
}
Ok(())
}
/// Tries to locate task enum based on the tasks impl target if attribute is not specified
/// but impl is present. If one is found, `task_enum` is set appropriately.
fn resolve_manual_task_enum(
tasks: &Option<tasks::TasksDef>,
task_enum: &mut Option<tasks::TaskEnumDef>,
items: &mut Vec<syn::Item>,
) -> syn::Result<()> {
let (None, Some(tasks)) = (&task_enum, &tasks) else { return Ok(()) };
let syn::Type::Path(type_path) = &*tasks.item_impl.self_ty else { return Ok(()) };
let type_path = type_path.path.segments.iter().collect::<Vec<_>>();
let (Some(seg), None) = (type_path.get(0), type_path.get(1)) else { return Ok(()) };
let mut result = None;
for item in items {
let syn::Item::Enum(item_enum) = item else { continue };
if item_enum.ident == seg.ident {
result = Some(syn::parse2::<tasks::TaskEnumDef>(item_enum.to_token_stream())?);
// replace item with a no-op because it will be handled by the expansion of
// `task_enum`. We use a no-op instead of simply removing it from the vec
// so that any indices collected by `Def::try_from` remain accurate
*item = syn::Item::Verbatim(quote::quote!());
break;
}
}
*task_enum = result;
Ok(())
}
/// Tries to locate a manual tasks impl (an impl implementing a trait whose last path segment is
/// `Task`) in the event that one has not been found already via the attribute macro
pub fn resolve_manual_tasks_impl(
tasks: &mut Option<tasks::TasksDef>,
task_enum: &Option<tasks::TaskEnumDef>,
items: &Vec<syn::Item>,
) -> syn::Result<()> {
let None = tasks else { return Ok(()) };
let mut result = None;
for item in items {
let syn::Item::Impl(item_impl) = item else { continue };
let Some((_, path, _)) = &item_impl.trait_ else { continue };
let Some(trait_last_seg) = path.segments.last() else { continue };
let syn::Type::Path(target_path) = &*item_impl.self_ty else { continue };
let target_path = target_path.path.segments.iter().collect::<Vec<_>>();
let (Some(target_ident), None) = (target_path.get(0), target_path.get(1)) else {
continue;
};
let matches_task_enum = match task_enum {
Some(task_enum) => task_enum.item_enum.ident == target_ident.ident,
None => true,
};
if trait_last_seg.ident == "Task" && matches_task_enum {
result = Some(syn::parse2::<tasks::TasksDef>(item_impl.to_token_stream())?);
break;
}
}
*tasks = result;
Ok(())
}
/// Check that usage of trait `Config` is consistent with the definition, i.e. it is used with
/// instance iff it is defined with instance.
fn check_instance_usage(&self) -> syn::Result<()> {
let mut instances = vec![];
instances.extend_from_slice(&self.pezpallet_struct.instances[..]);
instances.extend(&mut self.storages.iter().flat_map(|s| s.instances.clone()));
if let Some(call) = &self.call {
instances.extend_from_slice(&call.instances[..]);
}
if let Some(hooks) = &self.hooks {
instances.extend_from_slice(&hooks.instances[..]);
}
if let Some(event) = &self.event {
instances.extend_from_slice(&event.instances[..]);
}
if let Some(error) = &self.error {
instances.extend_from_slice(&error.instances[..]);
}
if let Some(inherent) = &self.inherent {
instances.extend_from_slice(&inherent.instances[..]);
}
if let Some(origin) = &self.origin {
instances.extend_from_slice(&origin.instances[..]);
}
if let Some(genesis_config) = &self.genesis_config {
instances.extend_from_slice(&genesis_config.instances[..]);
}
if let Some(genesis_build) = &self.genesis_build {
genesis_build.instances.as_ref().map(|i| instances.extend_from_slice(&i));
}
if let Some(extra_constants) = &self.extra_constants {
instances.extend_from_slice(&extra_constants.instances[..]);
}
if let Some(task_enum) = &self.task_enum {
instances.push(task_enum.instance_usage.clone());
}
let mut errors = instances.into_iter().filter_map(|instances| {
if instances.has_instance == self.config.has_instance {
return None;
}
let msg = if self.config.has_instance {
"Invalid generic declaration, trait is defined with instance but generic use none"
} else {
"Invalid generic declaration, trait is defined without instance but generic use \
some"
};
Some(syn::Error::new(instances.span, msg))
});
if let Some(mut first_error) = errors.next() {
for error in errors {
first_error.combine(error)
}
Err(first_error)
} else {
Ok(())
}
}
/// Depending on if pallet is instantiable:
/// * either `T: Config`
/// * or `T: Config<I>, I: 'static`
pub fn type_impl_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream {
if self.config.has_instance {
quote::quote_spanned!(span => T: Config<I>, I: 'static)
} else {
quote::quote_spanned!(span => T: Config)
}
}
/// Depending on if pallet is instantiable:
/// * either `T: Config`
/// * or `T: Config<I>, I: 'static = ()`
pub fn type_decl_bounded_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream {
if self.config.has_instance {
quote::quote_spanned!(span => T: Config<I>, I: 'static = ())
} else {
quote::quote_spanned!(span => T: Config)
}
}
/// Depending on if pallet is instantiable:
/// * either `T`
/// * or `T, I = ()`
pub fn type_decl_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream {
if self.config.has_instance {
quote::quote_spanned!(span => T, I = ())
} else {
quote::quote_spanned!(span => T)
}
}
/// Depending on if pallet is instantiable:
/// * either ``
/// * or `<I>`
/// to be used when using pallet trait `Config`
pub fn trait_use_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream {
if self.config.has_instance {
quote::quote_spanned!(span => <I>)
} else {
quote::quote_spanned!(span => )
}
}
/// Depending on if pallet is instantiable:
/// * either `T`
/// * or `T, I`
pub fn type_use_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream {
if self.config.has_instance {
quote::quote_spanned!(span => T, I)
} else {
quote::quote_spanned!(span => T)
}
}
}
/// Some generic kind for type which can be not generic, or generic over config,
/// or generic over config and instance, but not generic only over instance.
pub enum GenericKind {
None,
Config,
ConfigAndInstance,
}
impl GenericKind {
/// Return Err if it is only generics over instance but not over config.
pub fn from_gens(has_config: bool, has_instance: bool) -> Result<Self, ()> {
match (has_config, has_instance) {
(false, false) => Ok(GenericKind::None),
(true, false) => Ok(GenericKind::Config),
(true, true) => Ok(GenericKind::ConfigAndInstance),
(false, true) => Err(()),
}
}
/// Return the generic to be used when using the type.
///
/// Depending on its definition it can be: ``, `T` or `T, I`
pub fn type_use_gen(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream {
match self {
GenericKind::None => quote::quote!(),
GenericKind::Config => quote::quote_spanned!(span => T),
GenericKind::ConfigAndInstance => quote::quote_spanned!(span => T, I),
}
}
/// Return the generic to be used in `impl<..>` when implementing on the type.
pub fn type_impl_gen(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream {
match self {
GenericKind::None => quote::quote!(),
GenericKind::Config => quote::quote_spanned!(span => T: Config),
GenericKind::ConfigAndInstance => {
quote::quote_spanned!(span => T: Config<I>, I: 'static)
},
}
}
/// Return whereas the type has some generic.
pub fn is_generic(&self) -> bool {
match self {
GenericKind::None => false,
GenericKind::Config | GenericKind::ConfigAndInstance => true,
}
}
}
/// List of additional token to be used for parsing.
mod keyword {
syn::custom_keyword!(origin);
syn::custom_keyword!(call);
syn::custom_keyword!(tasks_experimental);
syn::custom_keyword!(task_enum);
syn::custom_keyword!(task_list);
syn::custom_keyword!(task_condition);
syn::custom_keyword!(task_index);
syn::custom_keyword!(weight);
syn::custom_keyword!(event);
syn::custom_keyword!(config);
syn::custom_keyword!(with_default);
syn::custom_keyword!(without_automatic_metadata);
syn::custom_keyword!(pezframe_system_config);
syn::custom_keyword!(hooks);
syn::custom_keyword!(inherent);
syn::custom_keyword!(error);
syn::custom_keyword!(storage);
syn::custom_keyword!(genesis_build);
syn::custom_keyword!(genesis_config);
syn::custom_keyword!(validate_unsigned);
syn::custom_keyword!(type_value);
syn::custom_keyword!(pallet);
syn::custom_keyword!(extra_constants);
syn::custom_keyword!(composite_enum);
syn::custom_keyword!(view_functions);
}
/// The possible values for the `#[pallet::config]` attribute.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum ConfigValue {
/// `#[pallet::config(with_default)]`
WithDefault(keyword::with_default),
/// `#[pallet::config(without_automatic_metadata)]`
WithoutAutomaticMetadata(keyword::without_automatic_metadata),
/// `#[pallet::config(pezframe_system_config)]`
FrameSystemConfig(keyword::pezframe_system_config),
}
impl syn::parse::Parse for ConfigValue {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(keyword::with_default) {
input.parse().map(ConfigValue::WithDefault)
} else if lookahead.peek(keyword::without_automatic_metadata) {
input.parse().map(ConfigValue::WithoutAutomaticMetadata)
} else if lookahead.peek(keyword::pezframe_system_config) {
input.parse().map(ConfigValue::FrameSystemConfig)
} else {
Err(lookahead.error())
}
}
}
/// Parse attributes for item in pallet module
/// syntax must be `pallet::` (e.g. `#[pallet::config]`)
enum PalletAttr {
Config {
span: proc_macro2::Span,
with_default: bool,
without_automatic_metadata: bool,
pezframe_system_config: bool,
},
Pallet(proc_macro2::Span),
Hooks(proc_macro2::Span),
/// A `#[pallet::call]` with optional attributes to specialize the behaviour.
///
/// # Attributes
///
/// Each attribute `attr` can take the form of `#[pallet::call(attr = …)]` or
/// `#[pallet::call(attr(…))]`. The possible attributes are:
///
/// ## `weight`
///
/// Can be used to reduce the repetitive weight annotation in the trivial case. It accepts one
/// argument that is expected to be an implementation of the `WeightInfo` or something that
/// behaves syntactically equivalent. This allows to annotate a `WeightInfo` for all the calls.
/// Now each call does not need to specify its own `#[pallet::weight]` but can instead use the
/// one from the `#[pallet::call]` definition. So instead of having to write it on each call:
///
/// ```ignore
/// #[pallet::call]
/// impl<T: Config> Pallet<T> {
/// #[pallet::weight(T::WeightInfo::create())]
/// pub fn create(
/// ```
/// you can now omit it on the call itself, if the name of the weigh function matches the call:
///
/// ```ignore
/// #[pallet::call(weight = <T as crate::Config>::WeightInfo)]
/// impl<T: Config> Pallet<T> {
/// pub fn create(
/// ```
///
/// It is possible to use this syntax together with instantiated pallets by using `Config<I>`
/// instead.
///
/// ### Dev Mode
///
/// Normally the `dev_mode` sets all weights of calls without a `#[pallet::weight]` annotation
/// to zero. Now when there is a `weight` attribute on the `#[pallet::call]`, then that is used
/// instead of the zero weight. So to say: it works together with `dev_mode`.
RuntimeCall(Option<InheritedCallWeightAttr>, proc_macro2::Span),
Error(proc_macro2::Span),
Tasks(proc_macro2::Span),
TaskList(proc_macro2::Span),
TaskCondition(proc_macro2::Span),
TaskIndex(proc_macro2::Span),
RuntimeTask(proc_macro2::Span),
RuntimeEvent(proc_macro2::Span),
RuntimeOrigin(proc_macro2::Span),
Inherent(proc_macro2::Span),
Storage(proc_macro2::Span),
GenesisConfig(proc_macro2::Span),
GenesisBuild(proc_macro2::Span),
ValidateUnsigned(proc_macro2::Span),
TypeValue(proc_macro2::Span),
ExtraConstants(proc_macro2::Span),
Composite(proc_macro2::Span),
ViewFunctions(proc_macro2::Span),
}
impl PalletAttr {
fn span(&self) -> proc_macro2::Span {
match self {
Self::Config { span, .. } => *span,
Self::Pallet(span) => *span,
Self::Hooks(span) => *span,
Self::Tasks(span) => *span,
Self::TaskCondition(span) => *span,
Self::TaskIndex(span) => *span,
Self::TaskList(span) => *span,
Self::Error(span) => *span,
Self::RuntimeTask(span) => *span,
Self::RuntimeCall(_, span) => *span,
Self::RuntimeEvent(span) => *span,
Self::RuntimeOrigin(span) => *span,
Self::Inherent(span) => *span,
Self::Storage(span) => *span,
Self::GenesisConfig(span) => *span,
Self::GenesisBuild(span) => *span,
Self::ValidateUnsigned(span) => *span,
Self::TypeValue(span) => *span,
Self::ExtraConstants(span) => *span,
Self::Composite(span) => *span,
Self::ViewFunctions(span) => *span,
}
}
}
impl syn::parse::Parse for PalletAttr {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
input.parse::<syn::Token![#]>()?;
let content;
syn::bracketed!(content in input);
content.parse::<keyword::pallet>()?;
content.parse::<syn::Token![::]>()?;
let lookahead = content.lookahead1();
if lookahead.peek(keyword::config) {
let span = content.parse::<keyword::config>()?.span();
if content.peek(syn::token::Paren) {
let inside_config;
// Parse (with_default, without_automatic_metadata) attributes.
let _paren = syn::parenthesized!(inside_config in content);
let fields: syn::punctuated::Punctuated<ConfigValue, syn::Token![,]> =
inside_config.parse_terminated(ConfigValue::parse, syn::Token![,])?;
let config_values = fields.iter().collect::<Vec<_>>();
let mut with_default = false;
let mut without_automatic_metadata = false;
let mut pezframe_system_config = false;
for config in config_values {
match config {
ConfigValue::WithDefault(_) => {
if with_default {
return Err(syn::Error::new(
span,
"Invalid duplicated attribute for `#[pallet::config]`. Please remove duplicates: with_default.",
));
}
with_default = true;
},
ConfigValue::WithoutAutomaticMetadata(_) => {
if without_automatic_metadata {
return Err(syn::Error::new(
span,
"Invalid duplicated attribute for `#[pallet::config]`. Please remove duplicates: without_automatic_metadata.",
));
}
without_automatic_metadata = true;
},
ConfigValue::FrameSystemConfig(_) => {
if pezframe_system_config {
return Err(syn::Error::new(
span,
"Invalid duplicated attribute for `#[pallet::config]`. Please remove duplicates: pezframe_system_config.",
));
}
pezframe_system_config = true;
},
}
}
Ok(PalletAttr::Config {
span,
with_default,
without_automatic_metadata,
pezframe_system_config,
})
} else {
Ok(PalletAttr::Config {
span,
with_default: false,
without_automatic_metadata: false,
pezframe_system_config: false,
})
}
} else if lookahead.peek(keyword::pallet) {
Ok(PalletAttr::Pallet(content.parse::<keyword::pallet>()?.span()))
} else if lookahead.peek(keyword::hooks) {
Ok(PalletAttr::Hooks(content.parse::<keyword::hooks>()?.span()))
} else if lookahead.peek(keyword::call) {
let span = content.parse::<keyword::call>().expect("peeked").span();
let attr = match content.is_empty() {
true => None,
false => Some(InheritedCallWeightAttr::parse(&content)?),
};
Ok(PalletAttr::RuntimeCall(attr, span))
} else if lookahead.peek(keyword::tasks_experimental) {
Ok(PalletAttr::Tasks(content.parse::<keyword::tasks_experimental>()?.span()))
} else if lookahead.peek(keyword::task_enum) {
Ok(PalletAttr::RuntimeTask(content.parse::<keyword::task_enum>()?.span()))
} else if lookahead.peek(keyword::task_condition) {
Ok(PalletAttr::TaskCondition(content.parse::<keyword::task_condition>()?.span()))
} else if lookahead.peek(keyword::task_index) {
Ok(PalletAttr::TaskIndex(content.parse::<keyword::task_index>()?.span()))
} else if lookahead.peek(keyword::task_list) {
Ok(PalletAttr::TaskList(content.parse::<keyword::task_list>()?.span()))
} else if lookahead.peek(keyword::error) {
Ok(PalletAttr::Error(content.parse::<keyword::error>()?.span()))
} else if lookahead.peek(keyword::event) {
Ok(PalletAttr::RuntimeEvent(content.parse::<keyword::event>()?.span()))
} else if lookahead.peek(keyword::origin) {
Ok(PalletAttr::RuntimeOrigin(content.parse::<keyword::origin>()?.span()))
} else if lookahead.peek(keyword::inherent) {
Ok(PalletAttr::Inherent(content.parse::<keyword::inherent>()?.span()))
} else if lookahead.peek(keyword::storage) {
Ok(PalletAttr::Storage(content.parse::<keyword::storage>()?.span()))
} else if lookahead.peek(keyword::genesis_config) {
Ok(PalletAttr::GenesisConfig(content.parse::<keyword::genesis_config>()?.span()))
} else if lookahead.peek(keyword::genesis_build) {
Ok(PalletAttr::GenesisBuild(content.parse::<keyword::genesis_build>()?.span()))
} else if lookahead.peek(keyword::validate_unsigned) {
Ok(PalletAttr::ValidateUnsigned(content.parse::<keyword::validate_unsigned>()?.span()))
} else if lookahead.peek(keyword::type_value) {
Ok(PalletAttr::TypeValue(content.parse::<keyword::type_value>()?.span()))
} else if lookahead.peek(keyword::extra_constants) {
Ok(PalletAttr::ExtraConstants(content.parse::<keyword::extra_constants>()?.span()))
} else if lookahead.peek(keyword::composite_enum) {
Ok(PalletAttr::Composite(content.parse::<keyword::composite_enum>()?.span()))
} else if lookahead.peek(keyword::view_functions) {
Ok(PalletAttr::ViewFunctions(content.parse::<keyword::view_functions>()?.span()))
} else {
Err(lookahead.error())
}
}
}
/// The optional weight annotation on a `#[pallet::call]` like `#[pallet::call(weight($type))]`.
#[derive(Clone)]
pub struct InheritedCallWeightAttr {
pub typename: syn::Type,
}
impl syn::parse::Parse for InheritedCallWeightAttr {
// Parses `(weight($type))` or `(weight = $type)`.
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let content;
syn::parenthesized!(content in input);
content.parse::<keyword::weight>()?;
let lookahead = content.lookahead1();
let buffer = if lookahead.peek(syn::token::Paren) {
let inner;
syn::parenthesized!(inner in content);
inner
} else if lookahead.peek(syn::Token![=]) {
content.parse::<syn::Token![=]>().expect("peeked");
content
} else {
return Err(lookahead.error());
};
Ok(Self { typename: buffer.parse()? })
}
}
@@ -0,0 +1,68 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::helper;
use syn::spanned::Spanned;
/// Definition of the pallet origin type.
///
/// Either:
/// * `type Origin`
/// * `struct Origin`
/// * `enum Origin`
pub struct OriginDef {
pub is_generic: bool,
/// A set of usage of instance, must be check for consistency with trait.
pub instances: Vec<helper::InstanceUsage>,
}
impl OriginDef {
pub fn try_from(item: &mut syn::Item) -> syn::Result<Self> {
let item_span = item.span();
let (vis, ident, generics) = match &item {
syn::Item::Enum(item) => (&item.vis, &item.ident, &item.generics),
syn::Item::Struct(item) => (&item.vis, &item.ident, &item.generics),
syn::Item::Type(item) => (&item.vis, &item.ident, &item.generics),
_ => {
let msg = "Invalid pallet::origin, expected enum or struct or type";
return Err(syn::Error::new(item.span(), msg));
},
};
let is_generic = !generics.params.is_empty();
let mut instances = vec![];
if let Some(u) = helper::check_type_def_optional_gen(generics, item.span())? {
instances.push(u);
} else {
// construct_runtime only allow generic event for instantiable pallet.
instances.push(helper::InstanceUsage { has_instance: false, span: ident.span() })
}
if !matches!(vis, syn::Visibility::Public(_)) {
let msg = "Invalid pallet::origin, Origin must be public";
return Err(syn::Error::new(item_span, msg));
}
if ident != "Origin" {
let msg = "Invalid pallet::origin, ident must `Origin`";
return Err(syn::Error::new(ident.span(), msg));
}
Ok(OriginDef { is_generic, instances })
}
}
@@ -0,0 +1,149 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::helper;
use quote::ToTokens;
use syn::spanned::Spanned;
/// List of additional token to be used for parsing.
mod keyword {
syn::custom_keyword!(pallet);
syn::custom_keyword!(Pallet);
syn::custom_keyword!(without_storage_info);
syn::custom_keyword!(storage_version);
}
/// Definition of the pallet pallet.
pub struct PalletStructDef {
/// The index of item in pallet pallet.
pub index: usize,
/// A set of usage of instance, must be check for consistency with config trait.
pub instances: Vec<helper::InstanceUsage>,
/// The keyword Pallet used (contains span).
pub pallet: keyword::Pallet,
/// The span of the pallet::pallet attribute.
pub attr_span: proc_macro2::Span,
/// Whether to specify the storages max encoded len when implementing `StorageInfoTrait`.
/// Contains the span of the attribute.
pub without_storage_info: Option<proc_macro2::Span>,
/// The in-code storage version of the pallet.
pub storage_version: Option<syn::Path>,
}
/// Parse for one variant of:
/// * `#[pallet::without_storage_info]`
/// * `#[pallet::storage_version(STORAGE_VERSION)]`
pub enum PalletStructAttr {
WithoutStorageInfoTrait(proc_macro2::Span),
StorageVersion { storage_version: syn::Path, span: proc_macro2::Span },
}
impl PalletStructAttr {
fn span(&self) -> proc_macro2::Span {
match self {
Self::WithoutStorageInfoTrait(span) | Self::StorageVersion { span, .. } => *span,
}
}
}
impl syn::parse::Parse for PalletStructAttr {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
input.parse::<syn::Token![#]>()?;
let content;
syn::bracketed!(content in input);
content.parse::<keyword::pallet>()?;
content.parse::<syn::Token![::]>()?;
let lookahead = content.lookahead1();
if lookahead.peek(keyword::without_storage_info) {
let span = content.parse::<keyword::without_storage_info>()?.span();
Ok(Self::WithoutStorageInfoTrait(span))
} else if lookahead.peek(keyword::storage_version) {
let span = content.parse::<keyword::storage_version>()?.span();
let version_content;
syn::parenthesized!(version_content in content);
let storage_version = version_content.parse::<syn::Path>()?;
Ok(Self::StorageVersion { storage_version, span })
} else {
Err(lookahead.error())
}
}
}
impl PalletStructDef {
pub fn try_from(
attr_span: proc_macro2::Span,
index: usize,
item: &mut syn::Item,
) -> syn::Result<Self> {
let item = if let syn::Item::Struct(item) = item {
item
} else {
let msg = "Invalid pallet::pallet, expected struct definition";
return Err(syn::Error::new(item.span(), msg));
};
let mut without_storage_info = None;
let mut storage_version_found = None;
let struct_attrs: Vec<PalletStructAttr> = helper::take_item_pallet_attrs(&mut item.attrs)?;
for attr in struct_attrs {
match attr {
PalletStructAttr::WithoutStorageInfoTrait(span)
if without_storage_info.is_none() =>
{
without_storage_info = Some(span);
},
PalletStructAttr::StorageVersion { storage_version, .. }
if storage_version_found.is_none() =>
{
storage_version_found = Some(storage_version);
},
attr => {
let msg = "Unexpected duplicated attribute";
return Err(syn::Error::new(attr.span(), msg));
},
}
}
let pallet = syn::parse2::<keyword::Pallet>(item.ident.to_token_stream())?;
if !matches!(item.vis, syn::Visibility::Public(_)) {
let msg = "Invalid pallet::pallet, Pallet must be public";
return Err(syn::Error::new(item.span(), msg));
}
if item.generics.where_clause.is_some() {
let msg = "Invalid pallet::pallet, where clause not supported on Pallet declaration";
return Err(syn::Error::new(item.generics.where_clause.span(), msg));
}
let instances =
vec![helper::check_type_def_gen_no_bounds(&item.generics, item.ident.span())?];
Ok(Self {
index,
instances,
pallet,
attr_span,
without_storage_info,
storage_version: storage_version_found,
})
}
}
@@ -0,0 +1,950 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::helper;
use pezframe_support_procedural_tools::get_doc_literals;
use quote::ToTokens;
use std::collections::HashMap;
use syn::spanned::Spanned;
/// List of additional token to be used for parsing.
mod keyword {
syn::custom_keyword!(Error);
syn::custom_keyword!(pallet);
syn::custom_keyword!(getter);
syn::custom_keyword!(storage_prefix);
syn::custom_keyword!(unbounded);
syn::custom_keyword!(whitelist_storage);
syn::custom_keyword!(disable_try_decode_storage);
syn::custom_keyword!(OptionQuery);
syn::custom_keyword!(ResultQuery);
syn::custom_keyword!(ValueQuery);
}
/// Parse for one of the following:
/// * `#[pallet::getter(fn dummy)]`
/// * `#[pallet::storage_prefix = "CustomName"]`
/// * `#[pallet::unbounded]`
/// * `#[pallet::whitelist_storage]
/// * `#[pallet::disable_try_decode_storage]`
pub enum PalletStorageAttr {
Getter(syn::Ident, proc_macro2::Span),
StorageName(syn::LitStr, proc_macro2::Span),
Unbounded(proc_macro2::Span),
WhitelistStorage(proc_macro2::Span),
DisableTryDecodeStorage(proc_macro2::Span),
}
impl PalletStorageAttr {
fn attr_span(&self) -> proc_macro2::Span {
match self {
Self::Getter(_, span) |
Self::StorageName(_, span) |
Self::Unbounded(span) |
Self::WhitelistStorage(span) => *span,
Self::DisableTryDecodeStorage(span) => *span,
}
}
}
impl syn::parse::Parse for PalletStorageAttr {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
input.parse::<syn::Token![#]>()?;
let attr_span = input.span();
let content;
syn::bracketed!(content in input);
content.parse::<keyword::pallet>()?;
content.parse::<syn::Token![::]>()?;
let lookahead = content.lookahead1();
if lookahead.peek(keyword::getter) {
content.parse::<keyword::getter>()?;
let generate_content;
syn::parenthesized!(generate_content in content);
generate_content.parse::<syn::Token![fn]>()?;
Ok(Self::Getter(generate_content.parse::<syn::Ident>()?, attr_span))
} else if lookahead.peek(keyword::storage_prefix) {
content.parse::<keyword::storage_prefix>()?;
content.parse::<syn::Token![=]>()?;
let renamed_prefix = content.parse::<syn::LitStr>()?;
// Ensure the renamed prefix is a proper Rust identifier
syn::parse_str::<syn::Ident>(&renamed_prefix.value()).map_err(|_| {
let msg = format!("`{}` is not a valid identifier", renamed_prefix.value());
syn::Error::new(renamed_prefix.span(), msg)
})?;
Ok(Self::StorageName(renamed_prefix, attr_span))
} else if lookahead.peek(keyword::unbounded) {
content.parse::<keyword::unbounded>()?;
Ok(Self::Unbounded(attr_span))
} else if lookahead.peek(keyword::whitelist_storage) {
content.parse::<keyword::whitelist_storage>()?;
Ok(Self::WhitelistStorage(attr_span))
} else if lookahead.peek(keyword::disable_try_decode_storage) {
content.parse::<keyword::disable_try_decode_storage>()?;
Ok(Self::DisableTryDecodeStorage(attr_span))
} else {
Err(lookahead.error())
}
}
}
struct PalletStorageAttrInfo {
getter: Option<syn::Ident>,
rename_as: Option<syn::LitStr>,
unbounded: bool,
whitelisted: bool,
try_decode: bool,
}
impl PalletStorageAttrInfo {
fn from_attrs(attrs: Vec<PalletStorageAttr>) -> syn::Result<Self> {
let mut getter = None;
let mut rename_as = None;
let mut unbounded = false;
let mut whitelisted = false;
let mut disable_try_decode_storage = false;
for attr in attrs {
match attr {
PalletStorageAttr::Getter(ident, ..) if getter.is_none() => getter = Some(ident),
PalletStorageAttr::StorageName(name, ..) if rename_as.is_none() =>
rename_as = Some(name),
PalletStorageAttr::Unbounded(..) if !unbounded => unbounded = true,
PalletStorageAttr::WhitelistStorage(..) if !whitelisted => whitelisted = true,
PalletStorageAttr::DisableTryDecodeStorage(..) if !disable_try_decode_storage =>
disable_try_decode_storage = true,
attr =>
return Err(syn::Error::new(
attr.attr_span(),
"Invalid attribute: Duplicate attribute",
)),
}
}
Ok(PalletStorageAttrInfo {
getter,
rename_as,
unbounded,
whitelisted,
try_decode: !disable_try_decode_storage,
})
}
}
/// The value and key types used by storages. Needed to expand metadata.
pub enum Metadata {
Value { value: syn::Type },
Map { value: syn::Type, key: syn::Type },
CountedMap { value: syn::Type, key: syn::Type },
DoubleMap { value: syn::Type, key1: syn::Type, key2: syn::Type },
NMap { keys: Vec<syn::Type>, keygen: syn::Type, value: syn::Type },
CountedNMap { keys: Vec<syn::Type>, keygen: syn::Type, value: syn::Type },
}
pub enum QueryKind {
OptionQuery,
ResultQuery(syn::Path, syn::Ident),
ValueQuery,
}
/// Definition of a storage, storage is a storage type like
/// `type MyStorage = StorageValue<MyStorageP, u32>`
/// The keys and values types are parsed in order to get metadata
pub struct StorageDef {
/// The index of storage item in pallet module.
pub index: usize,
/// Visibility of the storage type.
pub vis: syn::Visibility,
/// The type ident, to generate the StoragePrefix for.
pub ident: syn::Ident,
/// The keys and value metadata of the storage.
pub metadata: Metadata,
/// The doc associated to the storage.
pub docs: Vec<syn::Expr>,
/// A set of usage of instance, must be check for consistency with config.
pub instances: Vec<helper::InstanceUsage>,
/// Optional getter to generate. If some then query_kind is ensured to be some as well.
pub getter: Option<syn::Ident>,
/// Optional expression that evaluates to a type that can be used as StoragePrefix instead of
/// ident.
pub rename_as: Option<syn::LitStr>,
/// Whereas the querytype of the storage is OptionQuery, ResultQuery or ValueQuery.
/// Note that this is best effort as it can't be determined when QueryKind is generic, and
/// result can be false if user do some unexpected type alias.
pub query_kind: Option<QueryKind>,
/// Where clause of type definition.
pub where_clause: Option<syn::WhereClause>,
/// The span of the pallet::storage attribute.
pub attr_span: proc_macro2::Span,
/// The `cfg` attributes.
pub cfg_attrs: Vec<syn::Attribute>,
/// If generics are named (e.g. `StorageValue<Value = u32, ..>`) then this contains all the
/// generics of the storage.
/// If generics are not named, this is none.
pub named_generics: Option<StorageGenerics>,
/// If the value stored in this storage is unbounded.
pub unbounded: bool,
/// Whether or not reads to this storage key will be ignored by benchmarking
pub whitelisted: bool,
/// Whether or not to try to decode the storage key when running try-runtime checks.
pub try_decode: bool,
/// Whether or not a default hasher is allowed to replace `_`
pub use_default_hasher: bool,
/// Attributes
pub attrs: Vec<syn::Attribute>,
}
/// The parsed generic from the
#[derive(Clone)]
pub enum StorageGenerics {
DoubleMap {
hasher1: syn::Type,
key1: syn::Type,
hasher2: syn::Type,
key2: syn::Type,
value: syn::Type,
query_kind: Option<syn::Type>,
on_empty: Option<syn::Type>,
max_values: Option<syn::Type>,
},
Map {
hasher: syn::Type,
key: syn::Type,
value: syn::Type,
query_kind: Option<syn::Type>,
on_empty: Option<syn::Type>,
max_values: Option<syn::Type>,
},
CountedMap {
hasher: syn::Type,
key: syn::Type,
value: syn::Type,
query_kind: Option<syn::Type>,
on_empty: Option<syn::Type>,
max_values: Option<syn::Type>,
},
Value {
value: syn::Type,
query_kind: Option<syn::Type>,
on_empty: Option<syn::Type>,
},
NMap {
keygen: syn::Type,
value: syn::Type,
query_kind: Option<syn::Type>,
on_empty: Option<syn::Type>,
max_values: Option<syn::Type>,
},
CountedNMap {
keygen: syn::Type,
value: syn::Type,
query_kind: Option<syn::Type>,
on_empty: Option<syn::Type>,
max_values: Option<syn::Type>,
},
}
impl StorageGenerics {
/// Return the metadata from the defined generics
fn metadata(&self) -> syn::Result<Metadata> {
let res = match self.clone() {
Self::DoubleMap { value, key1, key2, .. } => Metadata::DoubleMap { value, key1, key2 },
Self::Map { value, key, .. } => Metadata::Map { value, key },
Self::CountedMap { value, key, .. } => Metadata::CountedMap { value, key },
Self::Value { value, .. } => Metadata::Value { value },
Self::NMap { keygen, value, .. } =>
Metadata::NMap { keys: collect_keys(&keygen)?, keygen, value },
Self::CountedNMap { keygen, value, .. } =>
Metadata::CountedNMap { keys: collect_keys(&keygen)?, keygen, value },
};
Ok(res)
}
/// Return the query kind from the defined generics
fn query_kind(&self) -> Option<syn::Type> {
match &self {
Self::DoubleMap { query_kind, .. } |
Self::Map { query_kind, .. } |
Self::CountedMap { query_kind, .. } |
Self::Value { query_kind, .. } |
Self::NMap { query_kind, .. } |
Self::CountedNMap { query_kind, .. } => query_kind.clone(),
}
}
}
enum StorageKind {
Value,
Map,
CountedMap,
DoubleMap,
NMap,
CountedNMap,
}
/// Check the generics in the `map` contains the generics in `gen` may contains generics in
/// `optional_gen`, and doesn't contains any other.
fn check_generics(
map: &HashMap<String, syn::AssocType>,
mandatory_generics: &[&str],
optional_generics: &[&str],
storage_type_name: &str,
args_span: proc_macro2::Span,
) -> syn::Result<()> {
let mut errors = vec![];
let expectation = {
let mut e = format!(
"`{}` expect generics {}and optional generics {}",
storage_type_name,
mandatory_generics
.iter()
.map(|name| format!("`{}`, ", name))
.collect::<String>(),
&optional_generics.iter().map(|name| format!("`{}`, ", name)).collect::<String>(),
);
e.pop();
e.pop();
e.push('.');
e
};
for (gen_name, gen_binding) in map {
if !mandatory_generics.contains(&gen_name.as_str()) &&
!optional_generics.contains(&gen_name.as_str())
{
let msg = format!(
"Invalid pallet::storage, Unexpected generic `{}` for `{}`. {}",
gen_name, storage_type_name, expectation,
);
errors.push(syn::Error::new(gen_binding.span(), msg));
}
}
for mandatory_generic in mandatory_generics {
if !map.contains_key(&mandatory_generic.to_string()) {
let msg = format!(
"Invalid pallet::storage, cannot find `{}` generic, required for `{}`.",
mandatory_generic, storage_type_name
);
errors.push(syn::Error::new(args_span, msg));
}
}
let mut errors = errors.drain(..);
if let Some(mut error) = errors.next() {
for other_error in errors {
error.combine(other_error);
}
Err(error)
} else {
Ok(())
}
}
/// Returns `(named generics, metadata, query kind, use_default_hasher)`
fn process_named_generics(
storage: &StorageKind,
args_span: proc_macro2::Span,
args: &[syn::AssocType],
dev_mode: bool,
) -> syn::Result<(Option<StorageGenerics>, Metadata, Option<syn::Type>, bool)> {
let mut parsed = HashMap::<String, syn::AssocType>::new();
// Ensure no duplicate.
for arg in args {
if let Some(other) = parsed.get(&arg.ident.to_string()) {
let msg = "Invalid pallet::storage, Duplicated named generic";
let mut err = syn::Error::new(arg.ident.span(), msg);
err.combine(syn::Error::new(other.ident.span(), msg));
return Err(err);
}
parsed.insert(arg.ident.to_string(), arg.clone());
}
let mut map_mandatory_generics = vec!["Key", "Value"];
let mut map_optional_generics = vec!["QueryKind", "OnEmpty", "MaxValues"];
if dev_mode {
map_optional_generics.push("Hasher");
} else {
map_mandatory_generics.push("Hasher");
}
let generics = match storage {
StorageKind::Value => {
check_generics(
&parsed,
&["Value"],
&["QueryKind", "OnEmpty"],
"StorageValue",
args_span,
)?;
StorageGenerics::Value {
value: parsed
.remove("Value")
.map(|binding| binding.ty)
.expect("checked above as mandatory generic"),
query_kind: parsed.remove("QueryKind").map(|binding| binding.ty),
on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty),
}
},
StorageKind::Map => {
check_generics(
&parsed,
&map_mandatory_generics,
&map_optional_generics,
"StorageMap",
args_span,
)?;
StorageGenerics::Map {
hasher: parsed
.remove("Hasher")
.map(|binding| binding.ty)
.unwrap_or(syn::parse_quote!(Blake2_128Concat)),
key: parsed
.remove("Key")
.map(|binding| binding.ty)
.expect("checked above as mandatory generic"),
value: parsed
.remove("Value")
.map(|binding| binding.ty)
.expect("checked above as mandatory generic"),
query_kind: parsed.remove("QueryKind").map(|binding| binding.ty),
on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty),
max_values: parsed.remove("MaxValues").map(|binding| binding.ty),
}
},
StorageKind::CountedMap => {
check_generics(
&parsed,
&map_mandatory_generics,
&map_optional_generics,
"CountedStorageMap",
args_span,
)?;
StorageGenerics::CountedMap {
hasher: parsed
.remove("Hasher")
.map(|binding| binding.ty)
.unwrap_or(syn::Type::Verbatim(quote::quote! { Blake2_128Concat })),
key: parsed
.remove("Key")
.map(|binding| binding.ty)
.expect("checked above as mandatory generic"),
value: parsed
.remove("Value")
.map(|binding| binding.ty)
.expect("checked above as mandatory generic"),
query_kind: parsed.remove("QueryKind").map(|binding| binding.ty),
on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty),
max_values: parsed.remove("MaxValues").map(|binding| binding.ty),
}
},
StorageKind::DoubleMap => {
let mut double_map_mandatory_generics = vec!["Key1", "Key2", "Value"];
if dev_mode {
map_optional_generics.extend(["Hasher1", "Hasher2"]);
} else {
double_map_mandatory_generics.extend(["Hasher1", "Hasher2"]);
}
check_generics(
&parsed,
&double_map_mandatory_generics,
&map_optional_generics,
"StorageDoubleMap",
args_span,
)?;
StorageGenerics::DoubleMap {
hasher1: parsed
.remove("Hasher1")
.map(|binding| binding.ty)
.unwrap_or(syn::parse_quote!(Blake2_128Concat)),
key1: parsed
.remove("Key1")
.map(|binding| binding.ty)
.expect("checked above as mandatory generic"),
hasher2: parsed
.remove("Hasher2")
.map(|binding| binding.ty)
.unwrap_or(syn::parse_quote!(Blake2_128Concat)),
key2: parsed
.remove("Key2")
.map(|binding| binding.ty)
.expect("checked above as mandatory generic"),
value: parsed
.remove("Value")
.map(|binding| binding.ty)
.expect("checked above as mandatory generic"),
query_kind: parsed.remove("QueryKind").map(|binding| binding.ty),
on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty),
max_values: parsed.remove("MaxValues").map(|binding| binding.ty),
}
},
StorageKind::NMap => {
check_generics(
&parsed,
&["Key", "Value"],
&["QueryKind", "OnEmpty", "MaxValues"],
"StorageNMap",
args_span,
)?;
StorageGenerics::NMap {
keygen: parsed
.remove("Key")
.map(|binding| binding.ty)
.expect("checked above as mandatory generic"),
value: parsed
.remove("Value")
.map(|binding| binding.ty)
.expect("checked above as mandatory generic"),
query_kind: parsed.remove("QueryKind").map(|binding| binding.ty),
on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty),
max_values: parsed.remove("MaxValues").map(|binding| binding.ty),
}
},
StorageKind::CountedNMap => {
check_generics(
&parsed,
&["Key", "Value"],
&["QueryKind", "OnEmpty", "MaxValues"],
"CountedStorageNMap",
args_span,
)?;
StorageGenerics::CountedNMap {
keygen: parsed
.remove("Key")
.map(|binding| binding.ty)
.expect("checked above as mandatory generic"),
value: parsed
.remove("Value")
.map(|binding| binding.ty)
.expect("checked above as mandatory generic"),
query_kind: parsed.remove("QueryKind").map(|binding| binding.ty),
on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty),
max_values: parsed.remove("MaxValues").map(|binding| binding.ty),
}
},
};
let metadata = generics.metadata()?;
let query_kind = generics.query_kind();
Ok((Some(generics), metadata, query_kind, false))
}
/// Returns `(named generics, metadata, query kind, use_default_hasher)`
fn process_unnamed_generics(
storage: &StorageKind,
args_span: proc_macro2::Span,
args: &[syn::Type],
dev_mode: bool,
) -> syn::Result<(Option<StorageGenerics>, Metadata, Option<syn::Type>, bool)> {
let retrieve_arg = |arg_pos| {
args.get(arg_pos).cloned().ok_or_else(|| {
let msg = format!(
"Invalid pallet::storage, unexpected number of generic argument, \
expect at least {} args, found {}.",
arg_pos + 1,
args.len(),
);
syn::Error::new(args_span, msg)
})
};
let prefix_arg = retrieve_arg(0)?;
syn::parse2::<syn::Token![_]>(prefix_arg.to_token_stream()).map_err(|e| {
let msg = "Invalid pallet::storage, for unnamed generic arguments the type \
first generic argument must be `_`, the argument is then replaced by macro.";
let mut err = syn::Error::new(prefix_arg.span(), msg);
err.combine(e);
err
})?;
let use_default_hasher = |arg_pos| {
let arg = retrieve_arg(arg_pos)?;
if syn::parse2::<syn::Token![_]>(arg.to_token_stream()).is_ok() {
if dev_mode {
Ok(true)
} else {
let msg = "`_` can only be used in dev_mode. Please specify an appropriate hasher.";
Err(syn::Error::new(arg.span(), msg))
}
} else {
Ok(false)
}
};
let res = match storage {
StorageKind::Value =>
(None, Metadata::Value { value: retrieve_arg(1)? }, retrieve_arg(2).ok(), false),
StorageKind::Map => (
None,
Metadata::Map { key: retrieve_arg(2)?, value: retrieve_arg(3)? },
retrieve_arg(4).ok(),
use_default_hasher(1)?,
),
StorageKind::CountedMap => (
None,
Metadata::CountedMap { key: retrieve_arg(2)?, value: retrieve_arg(3)? },
retrieve_arg(4).ok(),
use_default_hasher(1)?,
),
StorageKind::DoubleMap => (
None,
Metadata::DoubleMap {
key1: retrieve_arg(2)?,
key2: retrieve_arg(4)?,
value: retrieve_arg(5)?,
},
retrieve_arg(6).ok(),
use_default_hasher(1)? && use_default_hasher(3)?,
),
StorageKind::NMap => {
let keygen = retrieve_arg(1)?;
let keys = collect_keys(&keygen)?;
(
None,
Metadata::NMap { keys, keygen, value: retrieve_arg(2)? },
retrieve_arg(3).ok(),
false,
)
},
StorageKind::CountedNMap => {
let keygen = retrieve_arg(1)?;
let keys = collect_keys(&keygen)?;
(
None,
Metadata::CountedNMap { keys, keygen, value: retrieve_arg(2)? },
retrieve_arg(3).ok(),
false,
)
},
};
Ok(res)
}
/// Returns `(named generics, metadata, query kind, use_default_hasher)`
fn process_generics(
segment: &syn::PathSegment,
dev_mode: bool,
) -> syn::Result<(Option<StorageGenerics>, Metadata, Option<syn::Type>, bool)> {
let storage_kind = match &*segment.ident.to_string() {
"StorageValue" => StorageKind::Value,
"StorageMap" => StorageKind::Map,
"CountedStorageMap" => StorageKind::CountedMap,
"StorageDoubleMap" => StorageKind::DoubleMap,
"StorageNMap" => StorageKind::NMap,
"CountedStorageNMap" => StorageKind::CountedNMap,
found => {
let msg = format!(
"Invalid pallet::storage, expected ident: `StorageValue` or \
`StorageMap` or `CountedStorageMap` or `StorageDoubleMap` or `StorageNMap` or `CountedStorageNMap` \
in order to expand metadata, found `{}`.",
found,
);
return Err(syn::Error::new(segment.ident.span(), msg));
},
};
let args_span = segment.arguments.span();
let args = match &segment.arguments {
syn::PathArguments::AngleBracketed(args) if !args.args.is_empty() => args,
_ => {
let msg = "Invalid pallet::storage, invalid number of generic generic arguments, \
expect more that 0 generic arguments.";
return Err(syn::Error::new(segment.span(), msg));
},
};
if args.args.iter().all(|gen| matches!(gen, syn::GenericArgument::Type(_))) {
let args = args
.args
.iter()
.map(|gen| match gen {
syn::GenericArgument::Type(gen) => gen.clone(),
_ => unreachable!("It is asserted above that all generics are types"),
})
.collect::<Vec<_>>();
process_unnamed_generics(&storage_kind, args_span, &args, dev_mode)
} else if args.args.iter().all(|gen| matches!(gen, syn::GenericArgument::AssocType(_))) {
let args = args
.args
.iter()
.map(|gen| match gen {
syn::GenericArgument::AssocType(gen) => gen.clone(),
_ => unreachable!("It is asserted above that all generics are bindings"),
})
.collect::<Vec<_>>();
process_named_generics(&storage_kind, args_span, &args, dev_mode)
} else {
let msg = "Invalid pallet::storage, invalid generic declaration for storage. Expect only \
type generics or binding generics, e.g. `<Name1 = Gen1, Name2 = Gen2, ..>` or \
`<Gen1, Gen2, ..>`.";
Err(syn::Error::new(segment.span(), msg))
}
}
/// Parse the 2nd type argument to `StorageNMap` and return its keys.
fn collect_keys(keygen: &syn::Type) -> syn::Result<Vec<syn::Type>> {
if let syn::Type::Tuple(tup) = keygen {
tup.elems.iter().map(extract_key).collect::<syn::Result<Vec<_>>>()
} else {
Ok(vec![extract_key(keygen)?])
}
}
/// In `Key<H, K>`, extract K and return it.
fn extract_key(ty: &syn::Type) -> syn::Result<syn::Type> {
let typ = if let syn::Type::Path(typ) = ty {
typ
} else {
let msg = "Invalid pallet::storage, expected type path";
return Err(syn::Error::new(ty.span(), msg));
};
let key_struct = typ.path.segments.last().ok_or_else(|| {
let msg = "Invalid pallet::storage, expected type path with at least one segment";
syn::Error::new(typ.path.span(), msg)
})?;
if key_struct.ident != "Key" && key_struct.ident != "NMapKey" {
let msg = "Invalid pallet::storage, expected Key or NMapKey struct";
return Err(syn::Error::new(key_struct.ident.span(), msg));
}
let ty_params = if let syn::PathArguments::AngleBracketed(args) = &key_struct.arguments {
args
} else {
let msg = "Invalid pallet::storage, expected angle bracketed arguments";
return Err(syn::Error::new(key_struct.arguments.span(), msg));
};
if ty_params.args.len() != 2 {
let msg = format!(
"Invalid pallet::storage, unexpected number of generic arguments \
for Key struct, expected 2 args, found {}",
ty_params.args.len()
);
return Err(syn::Error::new(ty_params.span(), msg));
}
let key = match &ty_params.args[1] {
syn::GenericArgument::Type(key_ty) => key_ty.clone(),
_ => {
let msg = "Invalid pallet::storage, expected type";
return Err(syn::Error::new(ty_params.args[1].span(), msg));
},
};
Ok(key)
}
impl StorageDef {
/// Return the storage prefix for this storage item
pub fn prefix(&self) -> String {
self.rename_as
.as_ref()
.map(syn::LitStr::value)
.unwrap_or_else(|| self.ident.to_string())
}
/// Return either the span of the ident or the span of the literal in the
/// #[storage_prefix] attribute
pub fn prefix_span(&self) -> proc_macro2::Span {
self.rename_as
.as_ref()
.map(syn::LitStr::span)
.unwrap_or_else(|| self.ident.span())
}
pub fn try_from(
attr_span: proc_macro2::Span,
index: usize,
item: &mut syn::Item,
dev_mode: bool,
) -> syn::Result<Self> {
let item = if let syn::Item::Type(item) = item {
item
} else {
return Err(syn::Error::new(item.span(), "Invalid pallet::storage, expect item type."));
};
let attrs: Vec<PalletStorageAttr> = helper::take_item_pallet_attrs(&mut item.attrs)?;
let PalletStorageAttrInfo { getter, rename_as, mut unbounded, whitelisted, try_decode } =
PalletStorageAttrInfo::from_attrs(attrs)?;
// set all storages to be unbounded if dev_mode is enabled
unbounded |= dev_mode;
let cfg_attrs = helper::get_item_cfg_attrs(&item.attrs);
let instances = vec![helper::check_type_def_gen(&item.generics, item.ident.span())?];
let where_clause = item.generics.where_clause.clone();
let docs = get_doc_literals(&item.attrs);
let typ = if let syn::Type::Path(typ) = &*item.ty {
typ
} else {
let msg = "Invalid pallet::storage, expected type path";
return Err(syn::Error::new(item.ty.span(), msg));
};
if typ.path.segments.len() != 1 {
let msg = "Invalid pallet::storage, expected type path with one segment";
return Err(syn::Error::new(item.ty.span(), msg));
}
let (named_generics, metadata, query_kind, use_default_hasher) =
process_generics(&typ.path.segments[0], dev_mode)?;
let query_kind = query_kind
.map(|query_kind| {
use syn::{
AngleBracketedGenericArguments, GenericArgument, Path, PathArguments, Type,
TypePath,
};
let result_query = match query_kind {
Type::Path(path)
if path
.path
.segments
.last()
.map_or(false, |s| s.ident == "OptionQuery") =>
return Ok(Some(QueryKind::OptionQuery)),
Type::Path(TypePath { path: Path { segments, .. }, .. })
if segments.last().map_or(false, |s| s.ident == "ResultQuery") =>
segments
.last()
.expect("segments is checked to have the last value; qed")
.clone(),
Type::Path(path)
if path.path.segments.last().map_or(false, |s| s.ident == "ValueQuery") =>
return Ok(Some(QueryKind::ValueQuery)),
_ => return Ok(None),
};
let error_type = match result_query.arguments {
PathArguments::AngleBracketed(AngleBracketedGenericArguments {
args, ..
}) => {
if args.len() != 1 {
let msg = format!(
"Invalid pallet::storage, unexpected number of generic arguments \
for ResultQuery, expected 1 type argument, found {}",
args.len(),
);
return Err(syn::Error::new(args.span(), msg));
}
args[0].clone()
},
args => {
let msg = format!(
"Invalid pallet::storage, unexpected generic args for ResultQuery, \
expected angle-bracketed arguments, found `{}`",
args.to_token_stream().to_string()
);
return Err(syn::Error::new(args.span(), msg));
},
};
match error_type {
GenericArgument::Type(Type::Path(TypePath {
path: Path { segments: err_variant, leading_colon },
..
})) => {
if err_variant.len() < 2 {
let msg = format!(
"Invalid pallet::storage, unexpected number of path segments for \
the generics in ResultQuery, expected a path with at least 2 \
segments, found {}",
err_variant.len(),
);
return Err(syn::Error::new(err_variant.span(), msg));
}
let mut error = err_variant.clone();
let err_variant = error
.pop()
.expect("Checked to have at least 2; qed")
.into_value()
.ident;
// Necessary here to eliminate the last double colon
let last =
error.pop().expect("Checked to have at least 2; qed").into_value();
error.push_value(last);
Ok(Some(QueryKind::ResultQuery(
syn::Path { leading_colon, segments: error },
err_variant,
)))
},
gen_arg => {
let msg = format!(
"Invalid pallet::storage, unexpected generic argument kind, expected a \
type path to a `PalletError` enum variant, found `{}`",
gen_arg.to_token_stream().to_string(),
);
Err(syn::Error::new(gen_arg.span(), msg))
},
}
})
.transpose()?
.unwrap_or(Some(QueryKind::OptionQuery));
if let (None, Some(getter)) = (query_kind.as_ref(), getter.as_ref()) {
let msg = "Invalid pallet::storage, cannot generate getter because QueryKind is not \
identifiable. QueryKind must be `OptionQuery`, `ResultQuery`, `ValueQuery`, or default \
one to be identifiable.";
return Err(syn::Error::new(getter.span(), msg));
}
Ok(StorageDef {
attr_span,
index,
vis: item.vis.clone(),
ident: item.ident.clone(),
instances,
metadata,
docs,
getter,
rename_as,
query_kind,
where_clause,
cfg_attrs,
named_generics,
unbounded,
whitelisted,
try_decode,
use_default_hasher,
attrs: item.attrs.clone(),
})
}
}
@@ -0,0 +1,949 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Home of the parsing code for the Tasks API
use std::collections::HashSet;
#[cfg(test)]
use crate::assert_parse_error_matches;
#[cfg(test)]
use crate::pallet::parse::tests::simulate_manifest_dir;
use super::helper;
use derive_syn_parse::Parse;
use proc_macro2::TokenStream as TokenStream2;
use quote::{quote, ToTokens};
use syn::{
parse::ParseStream,
parse2,
spanned::Spanned,
token::{Bracket, Paren, PathSep, Pound},
Error, Expr, Ident, ImplItem, ImplItemFn, ItemEnum, ItemImpl, LitInt, PathArguments, Result,
TypePath,
};
pub mod keywords {
use syn::custom_keyword;
custom_keyword!(tasks_experimental);
custom_keyword!(task_enum);
custom_keyword!(task_list);
custom_keyword!(task_condition);
custom_keyword!(task_index);
custom_keyword!(task_weight);
custom_keyword!(pallet);
}
/// Represents the `#[pallet::tasks_experimental]` attribute and its attached item. Also includes
/// metadata about the linked [`TaskEnumDef`] if applicable.
#[derive(Clone, Debug)]
pub struct TasksDef {
pub tasks_attr: Option<PalletTasksAttr>,
pub tasks: Vec<TaskDef>,
pub item_impl: ItemImpl,
pub enum_ident: Ident,
pub enum_arguments: PathArguments,
}
impl syn::parse::Parse for TasksDef {
fn parse(input: ParseStream) -> Result<Self> {
let item_impl: ItemImpl = input.parse()?;
let (tasks_attrs, normal_attrs) = partition_tasks_attrs(&item_impl);
let tasks_attr = match tasks_attrs.first() {
Some(attr) => Some(parse2::<PalletTasksAttr>(attr.to_token_stream())?),
None => None,
};
if let Some(extra_tasks_attr) = tasks_attrs.get(1) {
return Err(Error::new(
extra_tasks_attr.span(),
"unexpected extra `#[pallet::tasks_experimental]` attribute",
));
}
let tasks: Vec<TaskDef> = if tasks_attr.is_some() {
item_impl
.items
.clone()
.into_iter()
.filter(|impl_item| matches!(impl_item, ImplItem::Fn(_)))
.map(|item| parse2::<TaskDef>(item.to_token_stream()))
.collect::<Result<_>>()?
} else {
Vec::new()
};
let mut task_indices = HashSet::<LitInt>::new();
for task in tasks.iter() {
let task_index = &task.index_attr.meta.index;
if !task_indices.insert(task_index.clone()) {
return Err(Error::new(
task_index.span(),
format!("duplicate task index `{}`", task_index),
));
}
}
let mut item_impl = item_impl;
item_impl.attrs = normal_attrs;
// we require the path on the impl to be a TypePath
let enum_path = parse2::<TypePath>(item_impl.self_ty.to_token_stream())?;
let segments = enum_path.path.segments.iter().collect::<Vec<_>>();
let (Some(last_seg), None) = (segments.get(0), segments.get(1)) else {
return Err(Error::new(
enum_path.span(),
"if specified manually, the task enum must be defined locally in this \
pallet and cannot be a re-export",
));
};
let enum_ident = last_seg.ident.clone();
let enum_arguments = last_seg.arguments.clone();
Ok(TasksDef { tasks_attr, item_impl, tasks, enum_ident, enum_arguments })
}
}
/// Parsing for a `#[pallet::tasks_experimental]` attr.
pub type PalletTasksAttr = PalletTaskAttr<keywords::tasks_experimental>;
/// Parsing for any of the attributes that can be used within a `#[pallet::tasks_experimental]`
/// [`ItemImpl`].
pub type TaskAttr = PalletTaskAttr<TaskAttrMeta>;
/// Parsing for a `#[pallet::task_index]` attr.
pub type TaskIndexAttr = PalletTaskAttr<TaskIndexAttrMeta>;
/// Parsing for a `#[pallet::task_condition]` attr.
pub type TaskConditionAttr = PalletTaskAttr<TaskConditionAttrMeta>;
/// Parsing for a `#[pallet::task_list]` attr.
pub type TaskListAttr = PalletTaskAttr<TaskListAttrMeta>;
/// Parsing for a `#[pallet::task_weight]` attr.
pub type TaskWeightAttr = PalletTaskAttr<TaskWeightAttrMeta>;
/// Parsing for a `#[pallet:task_enum]` attr.
pub type PalletTaskEnumAttr = PalletTaskAttr<keywords::task_enum>;
/// Parsing for a manually-specified (or auto-generated) task enum, optionally including the
/// attached `#[pallet::task_enum]` attribute.
#[derive(Clone)]
pub struct TaskEnumDef {
pub attr: Option<PalletTaskEnumAttr>,
pub item_enum: ItemEnum,
pub instance_usage: helper::InstanceUsage,
}
impl syn::parse::Parse for TaskEnumDef {
fn parse(input: ParseStream) -> Result<Self> {
let mut item_enum = input.parse::<ItemEnum>()?;
let attr = extract_pallet_attr(&mut item_enum)?;
let attr = match attr {
Some(attr) => Some(parse2(attr)?),
None => None,
};
let instance_usage =
helper::check_type_def_gen(&item_enum.generics, item_enum.ident.span())?;
Ok(TaskEnumDef { attr, item_enum, instance_usage })
}
}
/// Represents an individual tasks within a [`TasksDef`].
#[derive(Debug, Clone)]
pub struct TaskDef {
pub index_attr: TaskIndexAttr,
pub condition_attr: TaskConditionAttr,
pub list_attr: TaskListAttr,
pub weight_attr: TaskWeightAttr,
pub item: ImplItemFn,
pub arg_names: Vec<Ident>,
}
impl syn::parse::Parse for TaskDef {
fn parse(input: ParseStream) -> Result<Self> {
let item = input.parse::<ImplItemFn>()?;
// we only want to activate TaskAttrType parsing errors for tasks-related attributes,
// so we filter them here
let task_attrs = partition_task_attrs(&item).0;
let task_attrs: Vec<TaskAttr> = task_attrs
.into_iter()
.map(|attr| parse2(attr.to_token_stream()))
.collect::<Result<_>>()?;
let Some(index_attr) = task_attrs
.iter()
.find(|attr| matches!(attr.meta, TaskAttrMeta::TaskIndex(_)))
.cloned()
else {
return Err(Error::new(
item.sig.ident.span(),
"missing `#[pallet::task_index(..)]` attribute",
));
};
let Some(condition_attr) = task_attrs
.iter()
.find(|attr| matches!(attr.meta, TaskAttrMeta::TaskCondition(_)))
.cloned()
else {
return Err(Error::new(
item.sig.ident.span(),
"missing `#[pallet::task_condition(..)]` attribute",
));
};
let Some(list_attr) = task_attrs
.iter()
.find(|attr| matches!(attr.meta, TaskAttrMeta::TaskList(_)))
.cloned()
else {
return Err(Error::new(
item.sig.ident.span(),
"missing `#[pallet::task_list(..)]` attribute",
));
};
let Some(weight_attr) = task_attrs
.iter()
.find(|attr| matches!(attr.meta, TaskAttrMeta::TaskWeight(_)))
.cloned()
else {
return Err(Error::new(
item.sig.ident.span(),
"missing `#[pallet::task_weight(..)]` attribute",
));
};
if let Some(duplicate) = task_attrs
.iter()
.filter(|attr| matches!(attr.meta, TaskAttrMeta::TaskCondition(_)))
.collect::<Vec<_>>()
.get(1)
{
return Err(Error::new(
duplicate.span(),
"unexpected extra `#[pallet::task_condition(..)]` attribute",
));
}
if let Some(duplicate) = task_attrs
.iter()
.filter(|attr| matches!(attr.meta, TaskAttrMeta::TaskList(_)))
.collect::<Vec<_>>()
.get(1)
{
return Err(Error::new(
duplicate.span(),
"unexpected extra `#[pallet::task_list(..)]` attribute",
));
}
if let Some(duplicate) = task_attrs
.iter()
.filter(|attr| matches!(attr.meta, TaskAttrMeta::TaskIndex(_)))
.collect::<Vec<_>>()
.get(1)
{
return Err(Error::new(
duplicate.span(),
"unexpected extra `#[pallet::task_index(..)]` attribute",
));
}
let mut arg_names = vec![];
for input in item.sig.inputs.iter() {
match input {
syn::FnArg::Typed(pat_type) => match &*pat_type.pat {
syn::Pat::Ident(ident) => arg_names.push(ident.ident.clone()),
_ => return Err(Error::new(input.span(), "unexpected pattern type")),
},
_ => return Err(Error::new(input.span(), "unexpected function argument type")),
}
}
let index_attr = index_attr.try_into().expect("we check the type above; QED");
let condition_attr = condition_attr.try_into().expect("we check the type above; QED");
let list_attr = list_attr.try_into().expect("we check the type above; QED");
let weight_attr = weight_attr.try_into().expect("we check the type above; QED");
Ok(TaskDef { index_attr, condition_attr, list_attr, weight_attr, item, arg_names })
}
}
/// The contents of a [`TasksDef`]-related attribute.
#[derive(Parse, Debug, Clone)]
pub enum TaskAttrMeta {
#[peek(keywords::task_list, name = "#[pallet::task_list(..)]")]
TaskList(TaskListAttrMeta),
#[peek(keywords::task_index, name = "#[pallet::task_index(..)")]
TaskIndex(TaskIndexAttrMeta),
#[peek(keywords::task_condition, name = "#[pallet::task_condition(..)")]
TaskCondition(TaskConditionAttrMeta),
#[peek(keywords::task_weight, name = "#[pallet::task_weight(..)")]
TaskWeight(TaskWeightAttrMeta),
}
/// The contents of a `#[pallet::task_list]` attribute.
#[derive(Parse, Debug, Clone)]
pub struct TaskListAttrMeta {
pub task_list: keywords::task_list,
#[paren]
_paren: Paren,
#[inside(_paren)]
pub expr: Expr,
}
/// The contents of a `#[pallet::task_index]` attribute.
#[derive(Parse, Debug, Clone)]
pub struct TaskIndexAttrMeta {
pub task_index: keywords::task_index,
#[paren]
_paren: Paren,
#[inside(_paren)]
pub index: LitInt,
}
/// The contents of a `#[pallet::task_condition]` attribute.
#[derive(Parse, Debug, Clone)]
pub struct TaskConditionAttrMeta {
pub task_condition: keywords::task_condition,
#[paren]
_paren: Paren,
#[inside(_paren)]
pub expr: Expr,
}
/// The contents of a `#[pallet::task_weight]` attribute.
#[derive(Parse, Debug, Clone)]
pub struct TaskWeightAttrMeta {
pub task_weight: keywords::task_weight,
#[paren]
_paren: Paren,
#[inside(_paren)]
pub expr: Expr,
}
/// The contents of a `#[pallet::task]` attribute.
#[derive(Parse, Debug, Clone)]
pub struct PalletTaskAttr<T: syn::parse::Parse + core::fmt::Debug + ToTokens> {
pub pound: Pound,
#[bracket]
_bracket: Bracket,
#[inside(_bracket)]
pub pallet: keywords::pallet,
#[inside(_bracket)]
pub colons: PathSep,
#[inside(_bracket)]
pub meta: T,
}
impl ToTokens for TaskListAttrMeta {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let task_list = self.task_list;
let expr = &self.expr;
tokens.extend(quote!(#task_list(#expr)));
}
}
impl ToTokens for TaskConditionAttrMeta {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let task_condition = self.task_condition;
let expr = &self.expr;
tokens.extend(quote!(#task_condition(#expr)));
}
}
impl ToTokens for TaskWeightAttrMeta {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let task_weight = self.task_weight;
let expr = &self.expr;
tokens.extend(quote!(#task_weight(#expr)));
}
}
impl ToTokens for TaskIndexAttrMeta {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let task_index = self.task_index;
let index = &self.index;
tokens.extend(quote!(#task_index(#index)))
}
}
impl ToTokens for TaskAttrMeta {
fn to_tokens(&self, tokens: &mut TokenStream2) {
match self {
TaskAttrMeta::TaskList(list) => tokens.extend(list.to_token_stream()),
TaskAttrMeta::TaskIndex(index) => tokens.extend(index.to_token_stream()),
TaskAttrMeta::TaskCondition(condition) => tokens.extend(condition.to_token_stream()),
TaskAttrMeta::TaskWeight(weight) => tokens.extend(weight.to_token_stream()),
}
}
}
impl<T: syn::parse::Parse + core::fmt::Debug + ToTokens> ToTokens for PalletTaskAttr<T> {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let pound = self.pound;
let pallet = self.pallet;
let colons = self.colons;
let meta = &self.meta;
tokens.extend(quote!(#pound[#pallet #colons #meta]));
}
}
impl TryFrom<PalletTaskAttr<TaskAttrMeta>> for TaskIndexAttr {
type Error = syn::Error;
fn try_from(value: PalletTaskAttr<TaskAttrMeta>) -> Result<Self> {
let pound = value.pound;
let pallet = value.pallet;
let colons = value.colons;
match value.meta {
TaskAttrMeta::TaskIndex(meta) => parse2(quote!(#pound[#pallet #colons #meta])),
_ =>
return Err(Error::new(
value.span(),
format!("`{:?}` cannot be converted to a `TaskIndexAttr`", value.meta),
)),
}
}
}
impl TryFrom<PalletTaskAttr<TaskAttrMeta>> for TaskConditionAttr {
type Error = syn::Error;
fn try_from(value: PalletTaskAttr<TaskAttrMeta>) -> Result<Self> {
let pound = value.pound;
let pallet = value.pallet;
let colons = value.colons;
match value.meta {
TaskAttrMeta::TaskCondition(meta) => parse2(quote!(#pound[#pallet #colons #meta])),
_ =>
return Err(Error::new(
value.span(),
format!("`{:?}` cannot be converted to a `TaskConditionAttr`", value.meta),
)),
}
}
}
impl TryFrom<PalletTaskAttr<TaskAttrMeta>> for TaskWeightAttr {
type Error = syn::Error;
fn try_from(value: PalletTaskAttr<TaskAttrMeta>) -> Result<Self> {
let pound = value.pound;
let pallet = value.pallet;
let colons = value.colons;
match value.meta {
TaskAttrMeta::TaskWeight(meta) => parse2(quote!(#pound[#pallet #colons #meta])),
_ =>
return Err(Error::new(
value.span(),
format!("`{:?}` cannot be converted to a `TaskWeightAttr`", value.meta),
)),
}
}
}
impl TryFrom<PalletTaskAttr<TaskAttrMeta>> for TaskListAttr {
type Error = syn::Error;
fn try_from(value: PalletTaskAttr<TaskAttrMeta>) -> Result<Self> {
let pound = value.pound;
let pallet = value.pallet;
let colons = value.colons;
match value.meta {
TaskAttrMeta::TaskList(meta) => parse2(quote!(#pound[#pallet #colons #meta])),
_ =>
return Err(Error::new(
value.span(),
format!("`{:?}` cannot be converted to a `TaskListAttr`", value.meta),
)),
}
}
}
fn extract_pallet_attr(item_enum: &mut ItemEnum) -> Result<Option<TokenStream2>> {
let mut duplicate = None;
let mut attr = None;
item_enum.attrs = item_enum
.attrs
.iter()
.filter(|found_attr| {
let segs = found_attr
.path()
.segments
.iter()
.map(|seg| seg.ident.clone())
.collect::<Vec<_>>();
let (Some(seg1), Some(_), None) = (segs.get(0), segs.get(1), segs.get(2)) else {
return true;
};
if seg1 != "pallet" {
return true;
}
if attr.is_some() {
duplicate = Some(found_attr.span());
}
attr = Some(found_attr.to_token_stream());
false
})
.cloned()
.collect();
if let Some(span) = duplicate {
return Err(Error::new(span, "only one `#[pallet::_]` attribute is supported on this item"));
}
Ok(attr)
}
fn partition_tasks_attrs(item_impl: &ItemImpl) -> (Vec<syn::Attribute>, Vec<syn::Attribute>) {
item_impl.attrs.clone().into_iter().partition(|attr| {
let mut path_segs = attr.path().segments.iter();
let (Some(prefix), Some(suffix), None) =
(path_segs.next(), path_segs.next(), path_segs.next())
else {
return false;
};
prefix.ident == "pallet" && suffix.ident == "tasks_experimental"
})
}
fn partition_task_attrs(item: &ImplItemFn) -> (Vec<syn::Attribute>, Vec<syn::Attribute>) {
item.attrs.clone().into_iter().partition(|attr| {
let mut path_segs = attr.path().segments.iter();
let (Some(prefix), Some(suffix)) = (path_segs.next(), path_segs.next()) else {
return false;
};
// N.B: the `PartialEq` impl between `Ident` and `&str` is more efficient than
// parsing and makes no stack or heap allocations
prefix.ident == "pallet" &&
(suffix.ident == "tasks_experimental" ||
suffix.ident == "task_list" ||
suffix.ident == "task_condition" ||
suffix.ident == "task_weight" ||
suffix.ident == "task_index")
})
}
#[test]
fn test_parse_task_list_() {
parse2::<TaskAttr>(quote!(#[pallet::task_list(Something::iter())])).unwrap();
parse2::<TaskAttr>(quote!(#[pallet::task_list(Numbers::<T, I>::iter_keys())])).unwrap();
parse2::<TaskAttr>(quote!(#[pallet::task_list(iter())])).unwrap();
assert_parse_error_matches!(
parse2::<TaskAttr>(quote!(#[pallet::task_list()])),
"expected an expression"
);
assert_parse_error_matches!(
parse2::<TaskAttr>(quote!(#[pallet::task_list])),
"expected parentheses"
);
}
#[test]
fn test_parse_task_index() {
parse2::<TaskAttr>(quote!(#[pallet::task_index(3)])).unwrap();
parse2::<TaskAttr>(quote!(#[pallet::task_index(0)])).unwrap();
parse2::<TaskAttr>(quote!(#[pallet::task_index(17)])).unwrap();
assert_parse_error_matches!(
parse2::<TaskAttr>(quote!(#[pallet::task_index])),
"expected parentheses"
);
assert_parse_error_matches!(
parse2::<TaskAttr>(quote!(#[pallet::task_index("hey")])),
"expected integer literal"
);
assert_parse_error_matches!(
parse2::<TaskAttr>(quote!(#[pallet::task_index(0.3)])),
"expected integer literal"
);
}
#[test]
fn test_parse_task_condition() {
parse2::<TaskAttr>(quote!(#[pallet::task_condition(|x| x.is_some())])).unwrap();
parse2::<TaskAttr>(quote!(#[pallet::task_condition(|_x| some_expr())])).unwrap();
parse2::<TaskAttr>(quote!(#[pallet::task_condition(|| some_expr())])).unwrap();
parse2::<TaskAttr>(quote!(#[pallet::task_condition(some_expr())])).unwrap();
}
#[test]
fn test_parse_tasks_attr() {
parse2::<PalletTasksAttr>(quote!(#[pallet::tasks_experimental])).unwrap();
assert_parse_error_matches!(
parse2::<PalletTasksAttr>(quote!(#[pallet::taskss])),
"expected `tasks_experimental`"
);
assert_parse_error_matches!(
parse2::<PalletTasksAttr>(quote!(#[pallet::tasks_])),
"expected `tasks_experimental`"
);
assert_parse_error_matches!(
parse2::<PalletTasksAttr>(quote!(#[pal::tasks])),
"expected `pallet`"
);
assert_parse_error_matches!(
parse2::<PalletTasksAttr>(quote!(#[pallet::tasks_experimental()])),
"unexpected token"
);
}
#[test]
fn test_parse_tasks_def_basic() {
simulate_manifest_dir("../../examples/basic", || {
let parsed = parse2::<TasksDef>(quote! {
#[pallet::tasks_experimental]
impl<T: Config<I>, I: 'static> Pallet<T, I> {
/// Add a pair of numbers into the totals and remove them.
#[pallet::task_list(Numbers::<T, I>::iter_keys())]
#[pallet::task_condition(|i| Numbers::<T, I>::contains_key(i))]
#[pallet::task_index(0)]
#[pallet::task_weight(0)]
pub fn add_number_into_total(i: u32) -> DispatchResult {
let v = Numbers::<T, I>::take(i).ok_or(Error::<T, I>::NotFound)?;
Total::<T, I>::mutate(|(total_keys, total_values)| {
*total_keys += i;
*total_values += v;
});
Ok(())
}
}
})
.unwrap();
assert_eq!(parsed.tasks.len(), 1);
});
}
#[test]
fn test_parse_tasks_def_basic_increment_decrement() {
simulate_manifest_dir("../../examples/basic", || {
let parsed = parse2::<TasksDef>(quote! {
#[pallet::tasks_experimental]
impl<T: Config<I>, I: 'static> Pallet<T, I> {
/// Get the value and check if it can be incremented
#[pallet::task_index(0)]
#[pallet::task_condition(|| {
let value = Value::<T>::get().unwrap();
value < 255
})]
#[pallet::task_list(Vec::<Task<T>>::new())]
#[pallet::task_weight(0)]
fn increment() -> DispatchResult {
let value = Value::<T>::get().unwrap_or_default();
if value >= 255 {
Err(Error::<T>::ValueOverflow.into())
} else {
let new_val = value.checked_add(1).ok_or(Error::<T>::ValueOverflow)?;
Value::<T>::put(new_val);
Pallet::<T>::deposit_event(Event::Incremented { new_val });
Ok(())
}
}
// Get the value and check if it can be decremented
#[pallet::task_index(1)]
#[pallet::task_condition(|| {
let value = Value::<T>::get().unwrap();
value > 0
})]
#[pallet::task_list(Vec::<Task<T>>::new())]
#[pallet::task_weight(0)]
fn decrement() -> DispatchResult {
let value = Value::<T>::get().unwrap_or_default();
if value == 0 {
Err(Error::<T>::ValueUnderflow.into())
} else {
let new_val = value.checked_sub(1).ok_or(Error::<T>::ValueUnderflow)?;
Value::<T>::put(new_val);
Pallet::<T>::deposit_event(Event::Decremented { new_val });
Ok(())
}
}
}
})
.unwrap();
assert_eq!(parsed.tasks.len(), 2);
});
}
#[test]
fn test_parse_tasks_def_duplicate_index() {
simulate_manifest_dir("../../examples/basic", || {
assert_parse_error_matches!(
parse2::<TasksDef>(quote! {
#[pallet::tasks_experimental]
impl<T: Config<I>, I: 'static> Pallet<T, I> {
#[pallet::task_list(Something::iter())]
#[pallet::task_condition(|i| i % 2 == 0)]
#[pallet::task_index(0)]
#[pallet::task_weight(0)]
pub fn foo(i: u32) -> DispatchResult {
Ok(())
}
#[pallet::task_list(Numbers::<T, I>::iter_keys())]
#[pallet::task_condition(|i| Numbers::<T, I>::contains_key(i))]
#[pallet::task_index(0)]
#[pallet::task_weight(0)]
pub fn bar(i: u32) -> DispatchResult {
Ok(())
}
}
}),
"duplicate task index `0`"
);
});
}
#[test]
fn test_parse_tasks_def_missing_task_list() {
simulate_manifest_dir("../../examples/basic", || {
assert_parse_error_matches!(
parse2::<TasksDef>(quote! {
#[pallet::tasks_experimental]
impl<T: Config<I>, I: 'static> Pallet<T, I> {
#[pallet::task_condition(|i| i % 2 == 0)]
#[pallet::task_index(0)]
pub fn foo(i: u32) -> DispatchResult {
Ok(())
}
}
}),
r"missing `#\[pallet::task_list\(\.\.\)\]`"
);
});
}
#[test]
fn test_parse_tasks_def_missing_task_condition() {
simulate_manifest_dir("../../examples/basic", || {
assert_parse_error_matches!(
parse2::<TasksDef>(quote! {
#[pallet::tasks_experimental]
impl<T: Config<I>, I: 'static> Pallet<T, I> {
#[pallet::task_list(Something::iter())]
#[pallet::task_index(0)]
pub fn foo(i: u32) -> DispatchResult {
Ok(())
}
}
}),
r"missing `#\[pallet::task_condition\(\.\.\)\]`"
);
});
}
#[test]
fn test_parse_tasks_def_missing_task_index() {
simulate_manifest_dir("../../examples/basic", || {
assert_parse_error_matches!(
parse2::<TasksDef>(quote! {
#[pallet::tasks_experimental]
impl<T: Config<I>, I: 'static> Pallet<T, I> {
#[pallet::task_condition(|i| i % 2 == 0)]
#[pallet::task_list(Something::iter())]
pub fn foo(i: u32) -> DispatchResult {
Ok(())
}
}
}),
r"missing `#\[pallet::task_index\(\.\.\)\]`"
);
});
}
#[test]
fn test_parse_tasks_def_missing_task_weight() {
simulate_manifest_dir("../../examples/basic", || {
assert_parse_error_matches!(
parse2::<TasksDef>(quote! {
#[pallet::tasks_experimental]
impl<T: Config<I>, I: 'static> Pallet<T, I> {
#[pallet::task_condition(|i| i % 2 == 0)]
#[pallet::task_list(Something::iter())]
#[pallet::task_index(0)]
pub fn foo(i: u32) -> DispatchResult {
Ok(())
}
}
}),
r"missing `#\[pallet::task_weight\(\.\.\)\]`"
);
});
}
#[test]
fn test_parse_tasks_def_unexpected_extra_task_list_attr() {
simulate_manifest_dir("../../examples/basic", || {
assert_parse_error_matches!(
parse2::<TasksDef>(quote! {
#[pallet::tasks_experimental]
impl<T: Config<I>, I: 'static> Pallet<T, I> {
#[pallet::task_condition(|i| i % 2 == 0)]
#[pallet::task_index(0)]
#[pallet::task_weight(0)]
#[pallet::task_list(Something::iter())]
#[pallet::task_list(SomethingElse::iter())]
pub fn foo(i: u32) -> DispatchResult {
Ok(())
}
}
}),
r"unexpected extra `#\[pallet::task_list\(\.\.\)\]`"
);
});
}
#[test]
fn test_parse_tasks_def_unexpected_extra_task_condition_attr() {
simulate_manifest_dir("../../examples/basic", || {
assert_parse_error_matches!(
parse2::<TasksDef>(quote! {
#[pallet::tasks_experimental]
impl<T: Config<I>, I: 'static> Pallet<T, I> {
#[pallet::task_condition(|i| i % 2 == 0)]
#[pallet::task_condition(|i| i % 4 == 0)]
#[pallet::task_index(0)]
#[pallet::task_list(Something::iter())]
#[pallet::task_weight(0)]
pub fn foo(i: u32) -> DispatchResult {
Ok(())
}
}
}),
r"unexpected extra `#\[pallet::task_condition\(\.\.\)\]`"
);
});
}
#[test]
fn test_parse_tasks_def_unexpected_extra_task_index_attr() {
simulate_manifest_dir("../../examples/basic", || {
assert_parse_error_matches!(
parse2::<TasksDef>(quote! {
#[pallet::tasks_experimental]
impl<T: Config<I>, I: 'static> Pallet<T, I> {
#[pallet::task_condition(|i| i % 2 == 0)]
#[pallet::task_index(0)]
#[pallet::task_index(0)]
#[pallet::task_list(Something::iter())]
#[pallet::task_weight(0)]
pub fn foo(i: u32) -> DispatchResult {
Ok(())
}
}
}),
r"unexpected extra `#\[pallet::task_index\(\.\.\)\]`"
);
});
}
#[test]
fn test_parse_tasks_def_extra_tasks_attribute() {
simulate_manifest_dir("../../examples/basic", || {
assert_parse_error_matches!(
parse2::<TasksDef>(quote! {
#[pallet::tasks_experimental]
#[pallet::tasks_experimental]
impl<T: Config<I>, I: 'static> Pallet<T, I> {}
}),
r"unexpected extra `#\[pallet::tasks_experimental\]` attribute"
);
});
}
#[test]
fn test_parse_task_enum_def_basic() {
simulate_manifest_dir("../../examples/basic", || {
parse2::<TaskEnumDef>(quote! {
#[pallet::task_enum]
pub enum Task<T: Config> {
Increment,
Decrement,
}
})
.unwrap();
});
}
#[test]
fn test_parse_task_enum_def_non_task_name() {
simulate_manifest_dir("../../examples/basic", || {
parse2::<TaskEnumDef>(quote! {
#[pallet::task_enum]
pub enum Something<T> {
Foo
}
})
.unwrap();
});
}
#[test]
fn test_parse_task_enum_def_missing_attr_allowed() {
simulate_manifest_dir("../../examples/basic", || {
parse2::<TaskEnumDef>(quote! {
pub enum Task<T: Config> {
Increment,
Decrement,
}
})
.unwrap();
});
}
#[test]
fn test_parse_task_enum_def_missing_attr_alternate_name_allowed() {
simulate_manifest_dir("../../examples/basic", || {
parse2::<TaskEnumDef>(quote! {
pub enum Foo<T> {
Red,
}
})
.unwrap();
});
}
#[test]
fn test_parse_task_enum_def_wrong_attr() {
simulate_manifest_dir("../../examples/basic", || {
assert_parse_error_matches!(
parse2::<TaskEnumDef>(quote! {
#[pallet::something]
pub enum Task<T: Config> {
Increment,
Decrement,
}
}),
"expected `task_enum`"
);
});
}
#[test]
fn test_parse_task_enum_def_wrong_item() {
simulate_manifest_dir("../../examples/basic", || {
assert_parse_error_matches!(
parse2::<TaskEnumDef>(quote! {
#[pallet::task_enum]
pub struct Something<T>;
}),
"expected `enum`"
);
});
}
@@ -0,0 +1,296 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::{panic, sync::Mutex};
use syn::parse_quote;
#[doc(hidden)]
pub mod __private {
pub use regex;
}
/// Allows you to assert that the input expression resolves to an error whose string
/// representation matches the specified regex literal.
///
/// ## Example:
///
/// ```
/// use super::tasks::*;
///
/// assert_parse_error_matches!(
/// parse2::<TaskEnumDef>(quote! {
/// #[pallet::task_enum]
/// pub struct Something;
/// }),
/// "expected `enum`"
/// );
/// ```
///
/// More complex regular expressions are also possible (anything that could pass as a regex for
/// use with the [`regex`] crate.):
///
/// ```ignore
/// assert_parse_error_matches!(
/// parse2::<TasksDef>(quote! {
/// #[pallet::tasks_experimental]
/// impl<T: Config<I>, I: 'static> Pallet<T, I> {
/// #[pallet::task_condition(|i| i % 2 == 0)]
/// #[pallet::task_index(0)]
/// pub fn foo(i: u32) -> DispatchResult {
/// Ok(())
/// }
/// }
/// }),
/// r"missing `#\[pallet::task_list\(\.\.\)\]`"
/// );
/// ```
///
/// Although this is primarily intended to be used with parsing errors, this macro is general
/// enough that it will work with any error with a reasonable [`core::fmt::Display`] impl.
#[macro_export]
macro_rules! assert_parse_error_matches {
($expr:expr, $reg:literal) => {
match $expr {
Ok(_) => panic!("Expected an `Error(..)`, but got Ok(..)"),
Err(e) => {
let error_message = e.to_string();
let re = $crate::pallet::parse::tests::__private::regex::Regex::new($reg)
.expect("Invalid regex pattern");
assert!(
re.is_match(&error_message),
"Error message \"{}\" does not match the pattern \"{}\"",
error_message,
$reg
);
},
}
};
}
/// Allows you to assert that an entire pallet parses successfully. A custom syntax is used for
/// specifying arguments so please pay attention to the docs below.
///
/// The general syntax is:
///
/// ```ignore
/// assert_pallet_parses! {
/// #[manifest_dir("../../examples/basic")]
/// #[pezframe_support::pallet]
/// pub mod pallet {
/// #[pallet::config]
/// pub trait Config: pezframe_system::Config {}
///
/// #[pallet::pallet]
/// pub struct Pallet<T>(_);
/// }
/// };
/// ```
///
/// The `#[manifest_dir(..)]` attribute _must_ be specified as the _first_ attribute on the
/// pallet module, and should reference the relative (to your current directory) path of a
/// directory containing containing the `Cargo.toml` of a valid pallet. Typically you will only
/// ever need to use the `examples/basic` pallet, but sometimes it might be advantageous to
/// specify a different one that has additional dependencies.
///
/// The reason this must be specified is that our underlying parsing of pallets depends on
/// reaching out into the file system to look for particular `Cargo.toml` dependencies via the
/// [`generate_access_from_frame_or_crate`] method, so to simulate this properly in a proc
/// macro crate, we need to temporarily convince this function that we are running from the
/// directory of a valid pallet.
#[macro_export]
macro_rules! assert_pallet_parses {
(
#[manifest_dir($manifest_dir:literal)]
$($tokens:tt)*
) => {
{
let mut pallet: Option<$crate::pallet::parse::Def> = None;
$crate::pallet::parse::tests::simulate_manifest_dir($manifest_dir, core::panic::AssertUnwindSafe(|| {
pallet = Some($crate::pallet::parse::Def::try_from(syn::parse_quote! {
$($tokens)*
}, false).unwrap());
}));
pallet.unwrap()
}
}
}
/// Similar to [`assert_pallet_parses`], except this instead expects the pallet not to parse,
/// and allows you to specify a regex matching the expected parse error.
///
/// This is identical syntactically to [`assert_pallet_parses`] in every way except there is a
/// second attribute that must be specified immediately after `#[manifest_dir(..)]` which is
/// `#[error_regex(..)]` which should contain a string/regex literal designed to match what you
/// consider to be the correct parsing error we should see when we try to parse this particular
/// pallet.
///
/// ## Example:
///
/// ```
/// assert_pallet_parse_error! {
/// #[manifest_dir("../../examples/basic")]
/// #[error_regex("Missing `\\#\\[pallet::pallet\\]`")]
/// #[pezframe_support::pallet]
/// pub mod pallet {
/// #[pallet::config]
/// pub trait Config: pezframe_system::Config {}
/// }
/// }
/// ```
#[macro_export]
macro_rules! assert_pallet_parse_error {
(
#[manifest_dir($manifest_dir:literal)]
#[error_regex($reg:literal)]
$($tokens:tt)*
) => {
$crate::pallet::parse::tests::simulate_manifest_dir($manifest_dir, || {
$crate::assert_parse_error_matches!(
$crate::pallet::parse::Def::try_from(
parse_quote! {
$($tokens)*
},
false
),
$reg
);
});
}
}
/// Safely runs the specified `closure` while simulating an alternative `CARGO_MANIFEST_DIR`,
/// restoring `CARGO_MANIFEST_DIR` to its original value upon completion regardless of whether
/// the closure panics.
///
/// This is useful in tests of `Def::try_from` and other pezpallet-related methods that internally
/// make use of [`generate_access_from_frame_or_crate`], which is sensitive to entries in the
/// "current" `Cargo.toml` files.
///
/// This function uses a [`Mutex`] to avoid a race condition created when multiple tests try to
/// modify and then restore the `CARGO_MANIFEST_DIR` ENV var in an overlapping way.
pub fn simulate_manifest_dir<P: AsRef<std::path::Path>, F: FnOnce() + std::panic::UnwindSafe>(
path: P,
closure: F,
) {
use std::{env::*, path::*};
/// Ensures that only one thread can modify/restore the `CARGO_MANIFEST_DIR` ENV var at a time,
/// avoiding a race condition because `cargo test` runs tests in parallel.
///
/// Although this forces all tests that use [`simulate_manifest_dir`] to run sequentially with
/// respect to each other, this is still several orders of magnitude faster than using UI
/// tests, even if they are run in parallel.
static MANIFEST_DIR_LOCK: Mutex<()> = Mutex::new(());
// avoid race condition when swapping out `CARGO_MANIFEST_DIR`
let guard = MANIFEST_DIR_LOCK.lock().unwrap();
// obtain the current/original `CARGO_MANIFEST_DIR`
let orig = PathBuf::from(
var("CARGO_MANIFEST_DIR").expect("failed to read ENV var `CARGO_MANIFEST_DIR`"),
);
// set `CARGO_MANIFEST_DIR` to the provided path, relative to current working dir
set_var("CARGO_MANIFEST_DIR", orig.join(path.as_ref()));
// safely run closure catching any panics
let result = panic::catch_unwind(closure);
// restore original `CARGO_MANIFEST_DIR` before unwinding
set_var("CARGO_MANIFEST_DIR", &orig);
// unlock the mutex so we don't poison it if there is a panic
drop(guard);
// unwind any panics originally encountered when running closure
result.unwrap();
}
mod tasks;
#[test]
fn test_parse_minimal_pallet() {
assert_pallet_parses! {
#[manifest_dir("../../examples/basic")]
#[pezframe_support::pallet]
pub mod pallet {
#[pallet::config]
pub trait Config: pezframe_system::Config {}
#[pallet::pallet]
pub struct Pallet<T>(_);
}
};
}
#[test]
fn test_parse_pallet_missing_pallet() {
assert_pallet_parse_error! {
#[manifest_dir("../../examples/basic")]
#[error_regex("Missing `\\#\\[pallet::pallet\\]`")]
#[pezframe_support::pallet]
pub mod pallet {
#[pallet::config]
pub trait Config: pezframe_system::Config {}
}
}
}
#[test]
fn test_parse_pallet_missing_config() {
assert_pallet_parse_error! {
#[manifest_dir("../../examples/basic")]
#[error_regex("Missing `\\#\\[pallet::config\\]`")]
#[pezframe_support::pallet]
pub mod pallet {
#[pallet::pallet]
pub struct Pallet<T>(_);
}
}
}
#[test]
fn test_parse_pallet_deprecated_attribute_on_error_enum() {
assert_pallet_parse_error! {
#[manifest_dir("../../examples/basic")]
#[error_regex("The `\\#\\[deprecated\\]` attribute should be applied to individual variants, not the enum as a whole\\.")]
#[pezframe_support::pallet]
pub mod pallet {
#[pallet::error]
#[deprecated(note = "This enum is deprecated and should not be used.")]
pub enum Error<T> {
Foo
}
}
}
}
#[test]
fn test_parse_pallet_deprecated_attribute_on_event_enum() {
assert_pallet_parse_error! {
#[manifest_dir("../../examples/basic")]
#[error_regex("The `\\#\\[deprecated\\]` attribute should be applied to individual variants, not the enum as a whole\\.")]
#[pezframe_support::pallet]
pub mod pallet {
#[pallet::event]
#[deprecated(note = "This enum is deprecated and should not be used.")]
pub enum Event<T> {
Foo
}
}
}
}
@@ -0,0 +1,240 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use syn::parse_quote;
#[test]
fn test_parse_pallet_with_task_enum_missing_impl() {
assert_pallet_parse_error! {
#[manifest_dir("../../examples/basic")]
#[error_regex("Missing `\\#\\[pallet::tasks_experimental\\]` impl")]
#[pezframe_support::pallet]
pub mod pallet {
#[pallet::task_enum]
pub enum Task<T: Config> {
Something,
}
#[pallet::config]
pub trait Config: pezframe_system::Config {}
#[pallet::pallet]
pub struct Pallet<T>(_);
}
}
}
#[test]
fn test_parse_pallet_with_task_enum_wrong_attribute() {
assert_pallet_parse_error! {
#[manifest_dir("../../examples/basic")]
#[error_regex("expected one of")]
#[pezframe_support::pallet]
pub mod pallet {
#[pallet::wrong_attribute]
pub enum Task<T: Config> {
Something,
}
#[pallet::task_list]
impl<T: Config> pezframe_support::traits::Task for Task<T>
where
T: TypeInfo,
{}
#[pallet::config]
pub trait Config: pezframe_system::Config {}
#[pallet::pallet]
pub struct Pallet<T>(_);
}
}
}
#[test]
fn test_parse_pallet_missing_task_enum() {
assert_pallet_parses! {
#[manifest_dir("../../examples/basic")]
#[pezframe_support::pallet]
pub mod pallet {
#[pallet::tasks_experimental]
#[cfg(test)] // aha, this means it's being eaten
impl<T: Config> pezframe_support::traits::Task for Task<T>
where
T: TypeInfo,
{}
#[pallet::config]
pub trait Config: pezframe_system::Config {}
#[pallet::pallet]
pub struct Pallet<T>(_);
}
};
}
#[test]
fn test_parse_pallet_task_list_in_wrong_place() {
assert_pallet_parse_error! {
#[manifest_dir("../../examples/basic")]
#[error_regex("can only be used on items within an `impl` statement.")]
#[pezframe_support::pallet]
pub mod pallet {
pub enum MyCustomTaskEnum<T: Config> {
Something,
}
#[pallet::task_list]
pub fn something() {
println!("hey");
}
#[pallet::config]
pub trait Config: pezframe_system::Config {}
#[pallet::pallet]
pub struct Pallet<T>(_);
}
}
}
#[test]
fn test_parse_pallet_manual_tasks_impl_without_manual_tasks_enum() {
assert_pallet_parse_error! {
#[manifest_dir("../../examples/basic")]
#[error_regex(".*attribute must be attached to your.*")]
#[pezframe_support::pallet]
pub mod pallet {
impl<T: Config> pezframe_support::traits::Task for Task<T>
where
T: TypeInfo,
{
type Enumeration = alloc::vec::IntoIter<Task<T>>;
fn iter() -> Self::Enumeration {
alloc::vec![Task::increment, Task::decrement].into_iter()
}
}
#[pallet::config]
pub trait Config: pezframe_system::Config {}
#[pallet::pallet]
pub struct Pallet<T>(_);
}
}
}
#[test]
fn test_parse_pallet_manual_task_enum_non_manual_impl() {
assert_pallet_parses! {
#[manifest_dir("../../examples/basic")]
#[pezframe_support::pallet]
pub mod pallet {
pub enum MyCustomTaskEnum<T: Config> {
Something,
}
#[pallet::tasks_experimental]
impl<T: Config> pezframe_support::traits::Task for MyCustomTaskEnum<T>
where
T: TypeInfo,
{}
#[pallet::config]
pub trait Config: pezframe_system::Config {}
#[pallet::pallet]
pub struct Pallet<T>(_);
}
};
}
#[test]
fn test_parse_pallet_non_manual_task_enum_manual_impl() {
assert_pallet_parses! {
#[manifest_dir("../../examples/basic")]
#[pezframe_support::pallet]
pub mod pallet {
#[pallet::task_enum]
pub enum MyCustomTaskEnum<T: Config> {
Something,
}
impl<T: Config> pezframe_support::traits::Task for MyCustomTaskEnum<T>
where
T: TypeInfo,
{}
#[pallet::config]
pub trait Config: pezframe_system::Config {}
#[pallet::pallet]
pub struct Pallet<T>(_);
}
};
}
#[test]
fn test_parse_pallet_manual_task_enum_manual_impl() {
assert_pallet_parses! {
#[manifest_dir("../../examples/basic")]
#[pezframe_support::pallet]
pub mod pallet {
pub enum MyCustomTaskEnum<T: Config> {
Something,
}
impl<T: Config> pezframe_support::traits::Task for MyCustomTaskEnum<T>
where
T: TypeInfo,
{}
#[pallet::config]
pub trait Config: pezframe_system::Config {}
#[pallet::pallet]
pub struct Pallet<T>(_);
}
};
}
#[test]
fn test_parse_pallet_manual_task_enum_mismatch_ident() {
assert_pallet_parses! {
#[manifest_dir("../../examples/basic")]
#[pezframe_support::pallet]
pub mod pallet {
pub enum WrongIdent<T: Config> {
Something,
}
#[pallet::tasks_experimental]
impl<T: Config> pezframe_support::traits::Task for MyCustomTaskEnum<T>
where
T: TypeInfo,
{}
#[pallet::config]
pub trait Config: pezframe_system::Config {}
#[pallet::pallet]
pub struct Pallet<T>(_);
}
};
}
@@ -0,0 +1,104 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::helper;
use syn::spanned::Spanned;
/// Definition of type value. Just a function which is expanded to a struct implementing `Get`.
pub struct TypeValueDef {
/// The index of error item in pallet module.
pub index: usize,
/// Visibility of the struct to generate.
pub vis: syn::Visibility,
/// Ident of the struct to generate.
pub ident: syn::Ident,
/// The type return by Get.
pub type_: Box<syn::Type>,
/// If type value is generic over `T` (or `T` and `I` for instantiable pallet)
pub is_generic: bool,
/// The where clause of the function.
pub where_clause: Option<syn::WhereClause>,
/// The span of the pallet::type_value attribute.
pub attr_span: proc_macro2::Span,
/// Docs on the item.
pub docs: Vec<syn::Expr>,
}
impl TypeValueDef {
pub fn try_from(
attr_span: proc_macro2::Span,
index: usize,
item: &mut syn::Item,
) -> syn::Result<Self> {
let item = if let syn::Item::Fn(item) = item {
item
} else {
let msg = "Invalid pallet::type_value, expected item fn";
return Err(syn::Error::new(item.span(), msg));
};
let mut docs = vec![];
for attr in &item.attrs {
if let syn::Meta::NameValue(meta) = &attr.meta {
if meta.path.get_ident().map_or(false, |ident| ident == "doc") {
docs.push(meta.value.clone());
continue;
}
}
let msg = "Invalid pallet::type_value, unexpected attribute, only doc attribute are \
allowed";
return Err(syn::Error::new(attr.span(), msg));
}
if let Some(span) = item
.sig
.constness
.as_ref()
.map(|t| t.span())
.or_else(|| item.sig.asyncness.as_ref().map(|t| t.span()))
.or_else(|| item.sig.unsafety.as_ref().map(|t| t.span()))
.or_else(|| item.sig.abi.as_ref().map(|t| t.span()))
.or_else(|| item.sig.variadic.as_ref().map(|t| t.span()))
{
let msg = "Invalid pallet::type_value, unexpected token";
return Err(syn::Error::new(span, msg));
}
if !item.sig.inputs.is_empty() {
let msg = "Invalid pallet::type_value, unexpected argument";
return Err(syn::Error::new(item.sig.inputs[0].span(), msg));
}
let vis = item.vis.clone();
let ident = item.sig.ident.clone();
let type_ = match item.sig.output.clone() {
syn::ReturnType::Type(_, type_) => type_,
syn::ReturnType::Default => {
let msg = "Invalid pallet::type_value, expected return type";
return Err(syn::Error::new(item.sig.span(), msg));
},
};
helper::check_type_value_gen(&item.sig.generics, item.sig.span())?;
let is_generic = item.sig.generics.type_params().count() > 0;
let where_clause = item.sig.generics.where_clause.clone();
Ok(TypeValueDef { attr_span, index, is_generic, vis, ident, type_, where_clause, docs })
}
}
@@ -0,0 +1,55 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::helper;
use syn::spanned::Spanned;
/// The definition of the pallet validate unsigned implementation.
pub struct ValidateUnsignedDef {}
impl ValidateUnsignedDef {
pub fn try_from(item: &mut syn::Item) -> syn::Result<Self> {
let item = if let syn::Item::Impl(item) = item {
item
} else {
let msg = "Invalid pallet::validate_unsigned, expected item impl";
return Err(syn::Error::new(item.span(), msg));
};
if item.trait_.is_none() {
let msg = "Invalid pallet::validate_unsigned, expected impl<..> ValidateUnsigned for \
Pallet<..>";
return Err(syn::Error::new(item.span(), msg));
}
if let Some(last) = item.trait_.as_ref().unwrap().1.segments.last() {
if last.ident != "ValidateUnsigned" {
let msg = "Invalid pallet::validate_unsigned, expected trait ValidateUnsigned";
return Err(syn::Error::new(last.span(), msg));
}
} else {
let msg = "Invalid pallet::validate_unsigned, expected impl<..> ValidateUnsigned for \
Pallet<..>";
return Err(syn::Error::new(item.span(), msg));
}
helper::check_pallet_struct_usage(&item.self_ty)?;
helper::check_impl_gen(&item.generics, item.impl_token.span())?;
Ok(ValidateUnsignedDef {})
}
}
@@ -0,0 +1,152 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governsing permissions and
// limitations under the License.
use pezframe_support_procedural_tools::get_doc_literals;
use inflector::Inflector;
use syn::spanned::Spanned;
/// Parsed representation of an impl block annotated with `pallet::view_functions`.
pub struct ViewFunctionsImplDef {
/// The where_clause used.
pub where_clause: Option<syn::WhereClause>,
/// The span of the pallet::view_functions attribute.
pub attr_span: proc_macro2::Span,
/// The view function definitions.
pub view_functions: Vec<ViewFunctionDef>,
}
impl ViewFunctionsImplDef {
pub fn try_from(attr_span: proc_macro2::Span, item: &mut syn::Item) -> syn::Result<Self> {
let syn::Item::Impl(item_impl) = item else {
return Err(syn::Error::new(
item.span(),
"Invalid pallet::view_functions, expected item impl",
));
};
let mut view_functions = Vec::new();
for item in &mut item_impl.items {
if let syn::ImplItem::Fn(method) = item {
if !matches!(method.vis, syn::Visibility::Public(_)) {
let msg = "Invalid pallet::view_functions, view function must be public: \
`pub fn`";
let span = match method.vis {
syn::Visibility::Inherited => method.sig.span(),
_ => method.vis.span(),
};
return Err(syn::Error::new(span, msg));
}
let view_fn_def = ViewFunctionDef::try_from(method.clone())?;
view_functions.push(view_fn_def)
} else {
return Err(syn::Error::new(
item.span(),
"Invalid pallet::view_functions, expected a function",
));
}
}
Ok(Self {
view_functions,
attr_span,
where_clause: item_impl.generics.where_clause.clone(),
})
}
}
/// Parsed representation of a view function definition.
#[derive(Clone)]
pub struct ViewFunctionDef {
pub name: syn::Ident,
pub docs: Vec<syn::Expr>,
pub args: Vec<syn::FnArg>,
pub return_type: syn::Type,
}
impl TryFrom<syn::ImplItemFn> for ViewFunctionDef {
type Error = syn::Error;
fn try_from(method: syn::ImplItemFn) -> Result<Self, Self::Error> {
let syn::ReturnType::Type(_, type_) = method.sig.output else {
return Err(syn::Error::new(method.sig.span(), "view functions must return a value"));
};
Ok(Self {
name: method.sig.ident.clone(),
docs: get_doc_literals(&method.attrs),
args: method.sig.inputs.iter().cloned().collect::<Vec<_>>(),
return_type: *type_.clone(),
})
}
}
impl ViewFunctionDef {
pub fn view_function_struct_ident(&self) -> syn::Ident {
syn::Ident::new(
&format!("{}ViewFunction", self.name.to_string().to_pascal_case()),
self.name.span(),
)
}
pub fn view_function_id_suffix_bytes(&self) -> Result<[u8; 16], syn::Error> {
let mut output = [0u8; 16];
// concatenate the signature string
let arg_types = self
.args_names_types()?
.1
.iter()
.map(|ty| quote::quote!(#ty).to_string().replace(" ", ""))
.collect::<Vec<_>>()
.join(",");
let return_type = &self.return_type;
let return_type = quote::quote!(#return_type).to_string().replace(" ", "");
let view_fn_signature = format!(
"{view_function_name}({arg_types}) -> {return_type}",
view_function_name = &self.name,
);
// hash the signature string
let hash = pezsp_crypto_hashing::twox_128(view_fn_signature.as_bytes());
output.copy_from_slice(&hash[..]);
Ok(output)
}
pub fn args_names_types(&self) -> Result<(Vec<syn::Ident>, Vec<syn::Type>), syn::Error> {
Ok(self
.args
.iter()
.map(|arg| {
let syn::FnArg::Typed(pat_type) = arg else {
return Err(syn::Error::new(
arg.span(),
"Unsupported argument in view function",
));
};
let syn::Pat::Ident(ident) = &*pat_type.pat else {
return Err(syn::Error::new(
pat_type.pat.span(),
"Unsupported pattern in view function argument",
));
};
Ok((ident.ident.clone(), *pat_type.ty.clone()))
})
.collect::<Result<Vec<(syn::Ident, syn::Type)>, syn::Error>>()?
.into_iter()
.unzip())
}
}
@@ -0,0 +1,179 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use pezframe_support_procedural_tools::generate_access_from_frame_or_crate;
use quote::ToTokens;
// Derive `PalletError`
pub fn derive_pallet_error(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let syn::DeriveInput { ident: name, generics, data, .. } = match syn::parse(input) {
Ok(input) => input,
Err(e) => return e.to_compile_error().into(),
};
let pezframe_support = match generate_access_from_frame_or_crate("pezframe-support") {
Ok(c) => c,
Err(e) => return e.into_compile_error().into(),
};
let pezframe_support = &pezframe_support;
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let max_encoded_size = match data {
syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields {
syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) |
syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed: fields, .. }) => {
let maybe_field_tys = fields
.iter()
.map(|f| generate_field_types(f, &pezframe_support))
.collect::<syn::Result<Vec<_>>>();
let field_tys = match maybe_field_tys {
Ok(tys) => tys.into_iter().flatten(),
Err(e) => return e.into_compile_error().into(),
};
quote::quote! {
0_usize
#(
.saturating_add(<
#field_tys as #pezframe_support::traits::PalletError
>::MAX_ENCODED_SIZE)
)*
}
},
syn::Fields::Unit => quote::quote!(0),
},
syn::Data::Enum(syn::DataEnum { variants, .. }) => {
let field_tys = variants
.iter()
.map(|variant| generate_variant_field_types(variant, &pezframe_support))
.collect::<Result<Vec<Option<Vec<proc_macro2::TokenStream>>>, syn::Error>>();
let field_tys = match field_tys {
Ok(tys) => tys.into_iter().flatten().collect::<Vec<_>>(),
Err(e) => return e.to_compile_error().into(),
};
// We start with `1`, because the discriminant of an enum is stored as u8
if field_tys.is_empty() {
quote::quote!(1)
} else {
let variant_sizes = field_tys.into_iter().map(|variant_field_tys| {
quote::quote! {
1_usize
#(.saturating_add(<
#variant_field_tys as #pezframe_support::traits::PalletError
>::MAX_ENCODED_SIZE))*
}
});
quote::quote! {{
let mut size = 1_usize;
let mut tmp = 0_usize;
#(
tmp = #variant_sizes;
size = if tmp > size { tmp } else { size };
tmp = 0_usize;
)*
size
}}
}
},
syn::Data::Union(syn::DataUnion { union_token, .. }) => {
let msg = "Cannot derive `PalletError` for union; please implement it directly";
return syn::Error::new(union_token.span, msg).into_compile_error().into();
},
};
quote::quote!(
#[allow(deprecated)]
const _: () = {
impl #impl_generics #pezframe_support::traits::PalletError
for #name #ty_generics #where_clause
{
const MAX_ENCODED_SIZE: usize = #max_encoded_size;
}
};
)
.into()
}
fn generate_field_types(
field: &syn::Field,
scrate: &syn::Path,
) -> syn::Result<Option<proc_macro2::TokenStream>> {
let attrs = &field.attrs;
for attr in attrs {
if attr.path().is_ident("codec") {
let mut res = None;
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("skip") {
res = Some(None);
} else if meta.path.is_ident("compact") {
let field_ty = &field.ty;
res = Some(Some(quote::quote!(#scrate::__private::codec::Compact<#field_ty>)));
} else if meta.path.is_ident("compact") {
res = Some(Some(meta.value()?.parse()?));
}
Ok(())
})?;
if let Some(v) = res {
return Ok(v);
}
}
}
Ok(Some(field.ty.to_token_stream()))
}
fn generate_variant_field_types(
variant: &syn::Variant,
scrate: &syn::Path,
) -> syn::Result<Option<Vec<proc_macro2::TokenStream>>> {
let attrs = &variant.attrs;
for attr in attrs {
if attr.path().is_ident("codec") {
let mut skip = false;
// We ignore the error intentionally as this isn't `codec(skip)` when
// `parse_nested_meta` fails.
let _ = attr.parse_nested_meta(|meta| {
skip = meta.path.is_ident("skip");
Ok(())
});
if skip {
return Ok(None);
}
}
}
match &variant.fields {
syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) |
syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed: fields, .. }) => {
let field_tys = fields
.iter()
.map(|field| generate_field_types(field, scrate))
.collect::<syn::Result<Vec<_>>>()?;
Ok(Some(field_tys.into_iter().flatten().collect()))
},
syn::Fields::Unit => Ok(None),
}
}
@@ -0,0 +1,332 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::parse::runtime_types::RuntimeType;
use crate::{
construct_runtime::{
check_pallet_number, decl_all_pallets, decl_integrity_test, decl_pallet_runtime_setup,
decl_static_assertions, expand,
},
runtime::{
parse::{
AllPalletsDeclaration, ExplicitAllPalletsDeclaration, ImplicitAllPalletsDeclaration,
},
Def,
},
};
use cfg_expr::Predicate;
use pezframe_support_procedural_tools::{
generate_access_from_frame_or_crate, generate_crate_access, generate_hidden_includes,
};
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use std::collections::HashSet;
use syn::{Ident, Result};
/// The fixed name of the system pallet.
const SYSTEM_PALLET_NAME: &str = "System";
pub fn expand(def: Def, legacy_ordering: bool) -> TokenStream2 {
let input = def.input;
let (check_pallet_number_res, res) = match def.pallets {
AllPalletsDeclaration::Implicit(ref decl) => (
check_pallet_number(input.clone(), decl.pezpallet_count),
construct_runtime_implicit_to_explicit(input.into(), decl.clone(), legacy_ordering),
),
AllPalletsDeclaration::Explicit(ref decl) => (
check_pallet_number(input, decl.pallets.len()),
construct_runtime_final_expansion(
def.runtime_struct.ident.clone(),
decl.clone(),
def.runtime_types.clone(),
legacy_ordering,
),
),
};
let res = res.unwrap_or_else(|e| e.to_compile_error());
// We want to provide better error messages to the user and thus, handle the error here
// separately. If there is an error, we print the error and still generate all of the code to
// get in overall less errors for the user.
let res = if let Err(error) = check_pallet_number_res {
let error = error.to_compile_error();
quote! {
#error
#res
}
} else {
res
};
let res = expander::Expander::new("construct_runtime")
.dry(std::env::var("EXPAND_MACROS").is_err())
.verbose(true)
.write_to_out_dir(res)
.expect("Does not fail because of IO in OUT_DIR; qed");
res.into()
}
fn construct_runtime_implicit_to_explicit(
input: TokenStream2,
definition: ImplicitAllPalletsDeclaration,
legacy_ordering: bool,
) -> Result<TokenStream2> {
let pezframe_support = generate_access_from_frame_or_crate("pezframe-support")?;
let attr = if legacy_ordering { quote!((legacy_ordering)) } else { quote!() };
let mut expansion = quote::quote!(
#[#pezframe_support::runtime #attr]
#input
);
for pallet in definition.pezpallet_decls.iter() {
let pezpallet_path = &pallet.path;
let pezpallet_name = &pallet.name;
let runtime_param = &pallet.runtime_param;
let pezpallet_segment_and_instance = match (&pallet.pezpallet_segment, &pallet.instance) {
(Some(segment), Some(instance)) => quote::quote!(::#segment<#runtime_param, #instance>),
(Some(segment), None) => quote::quote!(::#segment<#runtime_param>),
(None, Some(instance)) => quote::quote!(<#instance>),
(None, None) => quote::quote!(),
};
expansion = quote::quote!(
#pezframe_support::__private::tt_call! {
macro = [{ #pezpallet_path::tt_default_parts_v2 }]
your_tt_return = [{ #pezframe_support::__private::tt_return }]
~~> #pezframe_support::match_and_insert! {
target = [{ #expansion }]
pattern = [{ #pezpallet_name = #pezpallet_path #pezpallet_segment_and_instance }]
}
}
);
}
Ok(expansion)
}
fn construct_runtime_final_expansion(
name: Ident,
definition: ExplicitAllPalletsDeclaration,
runtime_types: Vec<RuntimeType>,
legacy_ordering: bool,
) -> Result<TokenStream2> {
let ExplicitAllPalletsDeclaration { mut pallets, name: pallets_name } = definition;
if !legacy_ordering {
// Ensure that order of hooks is based on the pallet index
pallets.sort_by_key(|p| p.index);
}
let system_pallet =
pallets.iter().find(|decl| decl.name == SYSTEM_PALLET_NAME).ok_or_else(|| {
syn::Error::new(
pallets_name.span(),
"`System` pallet declaration is missing. \
Please add this line: `pub type System = pezframe_system;`",
)
})?;
if !system_pallet.cfg_pattern.is_empty() {
return Err(syn::Error::new(
system_pallet.name.span(),
"`System` pallet declaration is feature gated, please remove any `#[cfg]` attributes",
));
}
let features = pallets
.iter()
.filter_map(|decl| {
(!decl.cfg_pattern.is_empty()).then(|| {
decl.cfg_pattern.iter().flat_map(|attr| {
attr.predicates().filter_map(|pred| match pred {
Predicate::Feature(feat) => Some(feat),
Predicate::Test => Some("test"),
_ => None,
})
})
})
})
.flatten()
.collect::<HashSet<_>>();
let hidden_crate_name = "construct_runtime";
let scrate = generate_crate_access(hidden_crate_name, "pezframe-support");
let scrate_decl = generate_hidden_includes(hidden_crate_name, "pezframe-support");
let pezframe_system = generate_access_from_frame_or_crate("pezframe-system")?;
let block = quote!(<#name as #pezframe_system::Config>::Block);
let unchecked_extrinsic = quote!(<#block as #scrate::pezsp_runtime::traits::Block>::Extrinsic);
let mut dispatch = None;
let mut outer_event = None;
let mut outer_error = None;
let mut outer_origin = None;
let mut freeze_reason = None;
let mut hold_reason = None;
let mut slash_reason = None;
let mut lock_id = None;
let mut task = None;
let mut query = None;
for runtime_type in runtime_types.iter() {
match runtime_type {
RuntimeType::RuntimeCall(_) => {
dispatch =
Some(expand::expand_outer_dispatch(&name, system_pallet, &pallets, &scrate));
},
RuntimeType::RuntimeEvent(_) => {
outer_event = Some(expand::expand_outer_enum(
&name,
&pallets,
&scrate,
expand::OuterEnumType::Event,
)?);
},
RuntimeType::RuntimeError(_) => {
outer_error = Some(expand::expand_outer_enum(
&name,
&pallets,
&scrate,
expand::OuterEnumType::Error,
)?);
},
RuntimeType::RuntimeOrigin(_) => {
outer_origin =
Some(expand::expand_outer_origin(&name, system_pallet, &pallets, &scrate)?);
},
RuntimeType::RuntimeFreezeReason(_) => {
freeze_reason = Some(expand::expand_outer_freeze_reason(&pallets, &scrate));
},
RuntimeType::RuntimeHoldReason(_) => {
hold_reason = Some(expand::expand_outer_hold_reason(&pallets, &scrate));
},
RuntimeType::RuntimeSlashReason(_) => {
slash_reason = Some(expand::expand_outer_slash_reason(&pallets, &scrate));
},
RuntimeType::RuntimeLockId(_) => {
lock_id = Some(expand::expand_outer_lock_id(&pallets, &scrate));
},
RuntimeType::RuntimeTask(_) => {
task = Some(expand::expand_outer_task(&name, &pallets, &scrate));
},
RuntimeType::RuntimeViewFunction(_) => {
query = Some(expand::expand_outer_query(&name, &pallets, &scrate));
},
}
}
let all_pallets = decl_all_pallets(&name, pallets.iter(), &features);
let pezpallet_to_index = decl_pallet_runtime_setup(&name, &pallets, &scrate);
let metadata = expand::expand_runtime_metadata(
&name,
&pallets,
&scrate,
&unchecked_extrinsic,
&system_pallet.path,
);
let outer_config: TokenStream2 = expand::expand_outer_config(&name, &pallets, &scrate);
let inherent =
expand::expand_outer_inherent(&name, &block, &unchecked_extrinsic, &pallets, &scrate);
let validate_unsigned = expand::expand_outer_validate_unsigned(&name, &pallets, &scrate);
let integrity_test = decl_integrity_test(&scrate);
let static_assertions = decl_static_assertions(&name, &pallets, &scrate);
let res = quote!(
#scrate_decl
// Prevent UncheckedExtrinsic to print unused warning.
const _: () = {
#[allow(unused)]
type __HiddenUseOfUncheckedExtrinsic = #unchecked_extrinsic;
};
#[derive(
Clone, Copy, PartialEq, Eq, #scrate::pezsp_runtime::RuntimeDebug,
#scrate::__private::scale_info::TypeInfo
)]
pub struct #name;
impl #scrate::pezsp_runtime::traits::GetRuntimeBlockType for #name {
type RuntimeBlock = #block;
}
// Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata.
// The function is implemented by calling `impl_runtime_apis!`.
//
// However, the `runtime` may be used without calling `impl_runtime_apis!`.
// Rely on the `Deref` trait to differentiate between a runtime that implements
// APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro runtime).
//
// Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function.
// `InternalConstructRuntime` is implemented by the `runtime` for Runtime references (`& Runtime`),
// while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`).
//
// Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!`
// when both macros are called; and will resolve an empty `runtime_metadata` when only the `runtime`
// is used.
#[doc(hidden)]
trait InternalConstructRuntime {
#[inline(always)]
fn runtime_metadata(&self) -> #scrate::__private::Vec<#scrate::__private::metadata_ir::RuntimeApiMetadataIR> {
Default::default()
}
}
#[doc(hidden)]
impl InternalConstructRuntime for &#name {}
#outer_event
#outer_error
#outer_origin
#all_pallets
#pezpallet_to_index
#dispatch
#task
#query
#metadata
#outer_config
#inherent
#validate_unsigned
#freeze_reason
#hold_reason
#lock_id
#slash_reason
#integrity_test
#static_assertions
);
Ok(res)
}
@@ -0,0 +1,234 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Implementation of `runtime`.
//!
//! `runtime` implementation is recursive and can generate code which will call itself
//! in order to get all the pallet parts for each pallet.
//!
//! Pallets can define their parts:
//! - Implicitly: `pub type System = pezframe_system;`
//! - Explicitly: `pub type System = pezframe_system + Pallet + Call;`
//!
//! The `runtime` transitions from the implicit definition to the explicit one.
//! From the explicit state, Bizinikiwi expands the pallets with additional information
//! that is to be included in the runtime metadata.
//!
//! Pallets must provide the `tt_default_parts_v2` macro for these transitions.
//! These are automatically implemented by the `#[pallet::pallet]` macro.
//!
//! This macro also generates the following enums for ease of decoding if the respective type
//! is defined inside `#[runtime::derive]`:
//! - `enum RuntimeCall`: This type contains the information needed to decode extrinsics.
//! - `enum RuntimeEvent`: This type contains the information needed to decode events.
//! - `enum RuntimeError`: While this cannot be used directly to decode `pezsp_runtime::DispatchError`
//! from the chain, it contains the information needed to decode the
//! `pezsp_runtime::DispatchError::Module`.
//!
//! # State Transitions
//!
//! ```ignore
//! +----------+
//! | Implicit |
//! +----------+
//! |
//! v
//! +----------+
//! | Explicit |
//! +----------+
//! ```
//!
//! The `runtime` macro transforms the implicit declaration of each pallet
//! `System: pezframe_system` to an explicit one `System: pezframe_system + Pallet + Call` using the
//! `tt_default_parts_v2` macro.
//!
//! The `tt_default_parts_v2` macro exposes a plus separated list of pallet parts. For example, the
//! `Event` part is exposed only if the pallet implements an event via `#[pallet::event]` macro.
//! The tokens generated by this macro are `+ Pallet + Call` for our example.
//!
//! The `match_and_insert` macro takes in 3 arguments:
//! - target: This is the `TokenStream` that contains the `runtime` macro.
//! - pattern: The pattern to match against in the target stream.
//! - tokens: The tokens to added after the pattern match.
//!
//! The `runtime` macro uses the `tt_call` to get the default pallet parts via
//! the `tt_default_parts_v2` macro defined by each pallet. The pallet parts are then returned as
//! input to the `match_and_replace` macro.
//! The `match_and_replace` then will modify the `runtime` to expand the implicit
//! definition to the explicit one.
//!
//! For example,
//!
//! ```ignore
//! #[pezframe_support::runtime]
//! mod runtime {
//! //...
//!
//! #[runtime::pezpallet_index(0)]
//! pub type System = pezframe_system; // Implicit definition of parts
//!
//! #[runtime::pezpallet_index(1)]
//! pub type Balances = pezpallet_balances; // Implicit definition of parts
//! }
//! ```
//! This call has some implicit pallet parts, thus it will expand to:
//! ```ignore
//! pezframe_support::__private::tt_call! {
//! macro = [{ pezpallet_balances::tt_default_parts_v2 }]
//! ~~> pezframe_support::match_and_insert! {
//! target = [{
//! pezframe_support::__private::tt_call! {
//! macro = [{ pezframe_system::tt_default_parts_v2 }]
//! ~~> pezframe_support::match_and_insert! {
//! target = [{
//! #[pezframe_support::runtime]
//! mod runtime {
//! //...
//!
//! #[runtime::pezpallet_index(0)]
//! pub type System = pezframe_system;
//!
//! #[runtime::pezpallet_index(1)]
//! pub type Balances = pezpallet_balances;
//! }
//! }]
//! pattern = [{ System = pezframe_system }]
//! }
//! }
//! }]
//! pattern = [{ Balances = pezpallet_balances }]
//! }
//! }
//! ```
//! `tt_default_parts_v2` must be defined. It returns the pallet parts inside some tokens, and
//! then `tt_call` will pipe the returned pallet parts into the input of `match_and_insert`.
//! Thus `match_and_insert` will initially receive the following inputs:
//! ```ignore
//! pezframe_support::match_and_insert! {
//! target = [{
//! pezframe_support::match_and_insert! {
//! target = [{
//! #[pezframe_support::runtime]
//! mod runtime {
//! //...
//!
//! #[runtime::pezpallet_index(0)]
//! pub type System = pezframe_system;
//!
//! #[runtime::pezpallet_index(1)]
//! pub type Balances = pezpallet_balances;
//! }
//! }]
//! pattern = [{ System = pezframe_system }]
//! tokens = [{ ::{+ Pallet + Call} }]
//! }
//! }]
//! pattern = [{ Balances = pezpallet_balances }]
//! tokens = [{ ::{+ Pallet + Call} }]
//! }
//! ```
//! After dealing with `pezpallet_balances`, the inner `match_and_insert` will expand to:
//! ```ignore
//! pezframe_support::match_and_insert! {
//! target = [{
//! #[pezframe_support::runtime]
//! mod runtime {
//! //...
//!
//! #[runtime::pezpallet_index(0)]
//! pub type System = pezframe_system; // Implicit definition of parts
//!
//! #[runtime::pezpallet_index(1)]
//! pub type Balances = pezpallet_balances + Pallet + Call; // Explicit definition of parts
//! }
//! }]
//! pattern = [{ System = pezframe_system }]
//! tokens = [{ ::{+ Pallet + Call} }]
//! }
//! ```
//!
//! Which will then finally expand to the following:
//! ```ignore
//! #[pezframe_support::runtime]
//! mod runtime {
//! //...
//!
//! #[runtime::pezpallet_index(0)]
//! pub type System = pezframe_system + Pallet + Call;
//!
//! #[runtime::pezpallet_index(1)]
//! pub type Balances = pezpallet_balances + Pallet + Call;
//! }
//! ```
//!
//! This call has no implicit pallet parts, thus it will expand to the runtime construction:
//! ```ignore
//! pub struct Runtime { ... }
//! pub struct Call { ... }
//! impl Call ...
//! pub enum Origin { ... }
//! ...
//! ```
//!
//! Visualizing the entire flow of `#[pezframe_support::runtime]`, it would look like the following:
//!
//! ```ignore
//! +----------------------+ +------------------------+ +-------------------+
//! | | | (defined in pallet) | | |
//! | runtime | --> | tt_default_parts_v2! | --> | match_and_insert! |
//! | w/ no pallet parts | | | | |
//! +----------------------+ +------------------------+ +-------------------+
//!
//! +----------------------+
//! | |
//! --> | runtime |
//! | w/ pallet parts |
//! +----------------------+
//! ```
pub use parse::Def;
use proc_macro::TokenStream;
use syn::spanned::Spanned;
mod expand;
mod parse;
mod keyword {
syn::custom_keyword!(legacy_ordering);
}
pub fn runtime(attr: TokenStream, tokens: TokenStream) -> TokenStream {
let mut legacy_ordering = false;
if !attr.is_empty() {
if let Ok(_) = syn::parse::<keyword::legacy_ordering>(attr.clone()) {
legacy_ordering = true;
} else {
let msg = "Invalid runtime macro call: unexpected attribute. Macro call must be \
bare, such as `#[pezframe_support::runtime]` or `#[runtime]`, or must specify the \
`legacy_ordering` attribute, such as `#[pezframe_support::runtime(legacy_ordering)]` or \
#[runtime(legacy_ordering)].";
let span = proc_macro2::TokenStream::from(attr).span();
return syn::Error::new(span, msg).to_compile_error().into();
}
}
let item = syn::parse_macro_input!(tokens as syn::ItemMod);
match parse::Def::try_from(item) {
Ok(def) => expand::expand(def, legacy_ordering).into(),
Err(e) => e.to_compile_error().into(),
}
}
@@ -0,0 +1,37 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::pallet::parse::helper::MutItemAttrs;
use quote::ToTokens;
pub(crate) fn take_first_item_runtime_attr<Attr>(
item: &mut impl MutItemAttrs,
) -> syn::Result<Option<Attr>>
where
Attr: syn::parse::Parse,
{
let attrs = if let Some(attrs) = item.mut_item_attrs() { attrs } else { return Ok(None) };
if let Some(index) = attrs.iter().position(|attr| {
attr.path().segments.first().map_or(false, |segment| segment.ident == "runtime")
}) {
let runtime_attr = attrs.remove(index);
Ok(Some(syn::parse2(runtime_attr.into_token_stream())?))
} else {
Ok(None)
}
}
@@ -0,0 +1,286 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
pub mod helper;
pub mod pallet;
pub mod pezpallet_decl;
pub mod runtime_struct;
pub mod runtime_types;
use crate::construct_runtime::parse::Pallet;
use pezpallet_decl::PalletDeclaration;
use proc_macro2::TokenStream as TokenStream2;
use quote::ToTokens;
use std::collections::HashMap;
use syn::{spanned::Spanned, Ident, Token};
use pezframe_support_procedural_tools::syn_ext as ext;
use runtime_types::RuntimeType;
mod keyword {
use syn::custom_keyword;
custom_keyword!(runtime);
custom_keyword!(derive);
custom_keyword!(pezpallet_index);
custom_keyword!(disable_call);
custom_keyword!(disable_unsigned);
}
enum RuntimeAttr {
Runtime(proc_macro2::Span),
Derive(proc_macro2::Span, Vec<RuntimeType>),
PalletIndex(proc_macro2::Span, u8),
DisableCall(proc_macro2::Span),
DisableUnsigned(proc_macro2::Span),
}
impl RuntimeAttr {
fn span(&self) -> proc_macro2::Span {
match self {
Self::Runtime(span) => *span,
Self::Derive(span, _) => *span,
Self::PalletIndex(span, _) => *span,
Self::DisableCall(span) => *span,
Self::DisableUnsigned(span) => *span,
}
}
}
impl syn::parse::Parse for RuntimeAttr {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
input.parse::<syn::Token![#]>()?;
let content;
syn::bracketed!(content in input);
content.parse::<keyword::runtime>()?;
content.parse::<syn::Token![::]>()?;
let lookahead = content.lookahead1();
if lookahead.peek(keyword::runtime) {
Ok(RuntimeAttr::Runtime(content.parse::<keyword::runtime>()?.span()))
} else if lookahead.peek(keyword::derive) {
let _ = content.parse::<keyword::derive>();
let derive_content;
syn::parenthesized!(derive_content in content);
let runtime_types =
derive_content.parse::<ext::Punctuated<RuntimeType, Token![,]>>()?;
let runtime_types = runtime_types.inner.into_iter().collect();
Ok(RuntimeAttr::Derive(derive_content.span(), runtime_types))
} else if lookahead.peek(keyword::pezpallet_index) {
let _ = content.parse::<keyword::pezpallet_index>();
let pezpallet_index_content;
syn::parenthesized!(pezpallet_index_content in content);
let pezpallet_index = pezpallet_index_content.parse::<syn::LitInt>()?;
if !pezpallet_index.suffix().is_empty() {
let msg = "Number literal must not have a suffix";
return Err(syn::Error::new(pezpallet_index.span(), msg));
}
Ok(RuntimeAttr::PalletIndex(pezpallet_index.span(), pezpallet_index.base10_parse()?))
} else if lookahead.peek(keyword::disable_call) {
Ok(RuntimeAttr::DisableCall(content.parse::<keyword::disable_call>()?.span()))
} else if lookahead.peek(keyword::disable_unsigned) {
Ok(RuntimeAttr::DisableUnsigned(content.parse::<keyword::disable_unsigned>()?.span()))
} else {
Err(lookahead.error())
}
}
}
#[derive(Debug, Clone)]
pub enum AllPalletsDeclaration {
Implicit(ImplicitAllPalletsDeclaration),
Explicit(ExplicitAllPalletsDeclaration),
}
/// Declaration of a runtime with some pallet with implicit declaration of parts.
#[derive(Debug, Clone)]
pub struct ImplicitAllPalletsDeclaration {
pub pezpallet_decls: Vec<PalletDeclaration>,
pub pezpallet_count: usize,
}
/// Declaration of a runtime with all pallet having explicit declaration of parts.
#[derive(Debug, Clone)]
pub struct ExplicitAllPalletsDeclaration {
pub name: Ident,
pub pallets: Vec<Pallet>,
}
pub struct Def {
pub input: TokenStream2,
pub runtime_struct: runtime_struct::RuntimeStructDef,
pub pallets: AllPalletsDeclaration,
pub runtime_types: Vec<RuntimeType>,
}
impl Def {
pub fn try_from(mut item: syn::ItemMod) -> syn::Result<Self> {
let input: TokenStream2 = item.to_token_stream().into();
let item_span = item.span();
let items = &mut item
.content
.as_mut()
.ok_or_else(|| {
let msg = "Invalid runtime definition, expected mod to be inlined.";
syn::Error::new(item_span, msg)
})?
.1;
let mut runtime_struct = None;
let mut runtime_types = None;
let mut indices = HashMap::new();
let mut names = HashMap::new();
let mut pezpallet_decls = vec![];
let mut pallets = vec![];
for item in items.iter_mut() {
let mut pezpallet_index_and_item = None;
let mut disable_call = false;
let mut disable_unsigned = false;
while let Some(runtime_attr) =
helper::take_first_item_runtime_attr::<RuntimeAttr>(item)?
{
match runtime_attr {
RuntimeAttr::Runtime(_) if runtime_struct.is_none() => {
let p = runtime_struct::RuntimeStructDef::try_from(item)?;
runtime_struct = Some(p);
},
RuntimeAttr::Derive(_, types) if runtime_types.is_none() => {
runtime_types = Some(types);
},
RuntimeAttr::PalletIndex(span, index) => {
pezpallet_index_and_item = if let syn::Item::Type(item) = item {
Some((index, item.clone()))
} else {
let msg = "Invalid runtime::pezpallet_index, expected type definition";
return Err(syn::Error::new(span, msg));
};
},
RuntimeAttr::DisableCall(_) => disable_call = true,
RuntimeAttr::DisableUnsigned(_) => disable_unsigned = true,
attr => {
let msg = "Invalid duplicated attribute";
return Err(syn::Error::new(attr.span(), msg));
},
}
}
if let Some((pezpallet_index, pezpallet_item)) = pezpallet_index_and_item {
match *pezpallet_item.ty.clone() {
syn::Type::Path(ref path) => {
let pezpallet_decl =
PalletDeclaration::try_from(item.span(), &pezpallet_item, &path.path)?;
if let Some(used_pallet) =
names.insert(pezpallet_decl.name.clone(), pezpallet_decl.name.span())
{
let msg = "Two pallets with the same name!";
let mut err = syn::Error::new(used_pallet, &msg);
err.combine(syn::Error::new(pezpallet_decl.name.span(), &msg));
return Err(err);
}
pezpallet_decls.push(pezpallet_decl);
},
syn::Type::TraitObject(syn::TypeTraitObject { bounds, .. }) => {
let pallet = Pallet::try_from(
item.span(),
&pezpallet_item,
pezpallet_index,
disable_call,
disable_unsigned,
&bounds,
)?;
if let Some(used_pallet) = indices.insert(pallet.index, pallet.name.clone())
{
let msg = format!(
"Pallet indices are conflicting: Both pallets {} and {} are at index {}",
used_pallet, pallet.name, pallet.index,
);
let mut err = syn::Error::new(used_pallet.span(), &msg);
err.combine(syn::Error::new(pallet.name.span(), msg));
return Err(err);
}
pallets.push(pallet);
},
_ => continue,
}
} else {
if let syn::Item::Type(item) = item {
let msg = "Missing pallet index for pallet declaration. Please add `#[runtime::pezpallet_index(...)]`";
return Err(syn::Error::new(item.span(), &msg));
}
}
}
let name = item.ident.clone();
let decl_count = pezpallet_decls.len();
let pallets = if decl_count > 0 {
AllPalletsDeclaration::Implicit(ImplicitAllPalletsDeclaration {
pezpallet_decls,
pezpallet_count: decl_count.saturating_add(pallets.len()),
})
} else {
AllPalletsDeclaration::Explicit(ExplicitAllPalletsDeclaration { name, pallets })
};
let def = Def {
input,
runtime_struct: runtime_struct.ok_or_else(|| {
syn::Error::new(item_span,
"Missing Runtime. Please add a struct inside the module and annotate it with `#[runtime::runtime]`"
)
})?,
pallets,
runtime_types: runtime_types.ok_or_else(|| {
syn::Error::new(item_span,
"Missing Runtime Types. Please annotate the runtime struct with `#[runtime::derive]`"
)
})?,
};
Ok(def)
}
}
#[test]
fn runtime_parsing_works() {
let def = Def::try_from(syn::parse_quote! {
#[runtime::runtime]
mod runtime {
#[runtime::derive(RuntimeCall, RuntimeEvent)]
#[runtime::runtime]
pub struct Runtime;
#[runtime::pezpallet_index(0)]
pub type System = pezframe_system::Pallet<Runtime>;
#[runtime::pezpallet_index(1)]
pub type Pallet1 = pallet1<Instance1>;
}
})
.expect("Failed to parse runtime definition");
assert_eq!(def.runtime_struct.ident, "Runtime");
}
@@ -0,0 +1,199 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
construct_runtime::parse::{Pallet, PalletPart, PalletPartKeyword, PalletPath},
runtime::parse::PalletDeclaration,
};
use pezframe_support_procedural_tools::get_doc_literals;
use quote::ToTokens;
use syn::{punctuated::Punctuated, spanned::Spanned, token, Error};
impl Pallet {
pub fn try_from(
attr_span: proc_macro2::Span,
item: &syn::ItemType,
pezpallet_index: u8,
disable_call: bool,
disable_unsigned: bool,
bounds: &Punctuated<syn::TypeParamBound, token::Plus>,
) -> syn::Result<Self> {
let name = item.ident.clone();
let mut pezpallet_path = None;
let mut pezpallet_parts = vec![];
for (index, bound) in bounds.into_iter().enumerate() {
if let syn::TypeParamBound::Trait(syn::TraitBound { path, .. }) = bound {
if index == 0 {
pezpallet_path = Some(PalletPath { inner: path.clone() });
} else {
let pezpallet_part = syn::parse2::<PalletPart>(bound.into_token_stream())?;
pezpallet_parts.push(pezpallet_part);
}
} else {
return Err(Error::new(
attr_span,
"Invalid pallet declaration, expected a path or a trait object",
));
};
}
let mut path = pezpallet_path.ok_or(Error::new(
attr_span,
"Invalid pallet declaration, expected a path or a trait object",
))?;
let PalletDeclaration { path: inner, instance, .. } =
PalletDeclaration::try_from(attr_span, item, &path.inner)?;
path = PalletPath { inner };
pezpallet_parts = pezpallet_parts
.into_iter()
.filter(|part| {
if let (true, &PalletPartKeyword::Call(_)) = (disable_call, &part.keyword) {
false
} else if let (true, &PalletPartKeyword::ValidateUnsigned(_)) =
(disable_unsigned, &part.keyword)
{
false
} else {
true
}
})
.collect();
let cfg_pattern = item
.attrs
.iter()
.filter(|attr| attr.path().segments.first().map_or(false, |s| s.ident == "cfg"))
.map(|attr| {
attr.parse_args_with(|input: syn::parse::ParseStream| {
let input = input.parse::<proc_macro2::TokenStream>()?;
cfg_expr::Expression::parse(&input.to_string())
.map_err(|e| syn::Error::new(attr.span(), e.to_string()))
})
})
.collect::<syn::Result<Vec<_>>>()?;
let docs = get_doc_literals(&item.attrs);
Ok(Pallet {
is_expanded: true,
name,
index: pezpallet_index,
path,
instance,
cfg_pattern,
pezpallet_parts,
docs,
})
}
}
#[test]
fn pezpallet_parsing_works() {
use syn::{parse_quote, ItemType};
let item: ItemType = parse_quote! {
pub type System = pezframe_system + Call;
};
let ItemType { ty, .. } = item.clone();
let syn::Type::TraitObject(syn::TypeTraitObject { bounds, .. }) = *ty else {
panic!("Expected a trait object");
};
let index = 0;
let pallet =
Pallet::try_from(proc_macro2::Span::call_site(), &item, index, false, false, &bounds)
.unwrap();
assert_eq!(pallet.name.to_string(), "System");
assert_eq!(pallet.index, index);
assert_eq!(pallet.path.to_token_stream().to_string(), "pezframe_system");
assert_eq!(pallet.instance, None);
}
#[test]
fn pezpallet_parsing_works_with_instance() {
use syn::{parse_quote, ItemType};
let item: ItemType = parse_quote! {
pub type System = pezframe_system<Instance1> + Call;
};
let ItemType { ty, .. } = item.clone();
let syn::Type::TraitObject(syn::TypeTraitObject { bounds, .. }) = *ty else {
panic!("Expected a trait object");
};
let index = 0;
let pallet =
Pallet::try_from(proc_macro2::Span::call_site(), &item, index, false, false, &bounds)
.unwrap();
assert_eq!(pallet.name.to_string(), "System");
assert_eq!(pallet.index, index);
assert_eq!(pallet.path.to_token_stream().to_string(), "pezframe_system");
assert_eq!(pallet.instance, Some(parse_quote! { Instance1 }));
}
#[test]
fn pezpallet_parsing_works_with_pallet() {
use syn::{parse_quote, ItemType};
let item: ItemType = parse_quote! {
pub type System = pezframe_system::Pallet<Runtime> + Call;
};
let ItemType { ty, .. } = item.clone();
let syn::Type::TraitObject(syn::TypeTraitObject { bounds, .. }) = *ty else {
panic!("Expected a trait object");
};
let index = 0;
let pallet =
Pallet::try_from(proc_macro2::Span::call_site(), &item, index, false, false, &bounds)
.unwrap();
assert_eq!(pallet.name.to_string(), "System");
assert_eq!(pallet.index, index);
assert_eq!(pallet.path.to_token_stream().to_string(), "pezframe_system");
assert_eq!(pallet.instance, None);
}
#[test]
fn pezpallet_parsing_works_with_instance_and_pallet() {
use syn::{parse_quote, ItemType};
let item: ItemType = parse_quote! {
pub type System = pezframe_system::Pallet<Runtime, Instance1> + Call;
};
let ItemType { ty, .. } = item.clone();
let syn::Type::TraitObject(syn::TypeTraitObject { bounds, .. }) = *ty else {
panic!("Expected a trait object");
};
let index = 0;
let pallet =
Pallet::try_from(proc_macro2::Span::call_site(), &item, index, false, false, &bounds)
.unwrap();
assert_eq!(pallet.name.to_string(), "System");
assert_eq!(pallet.index, index);
assert_eq!(pallet.path.to_token_stream().to_string(), "pezframe_system");
assert_eq!(pallet.instance, Some(parse_quote! { Instance1 }));
}
@@ -0,0 +1,172 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use syn::{Ident, PathArguments};
/// The declaration of a pallet.
#[derive(Debug, Clone)]
pub struct PalletDeclaration {
/// The name of the pallet, e.g.`System` in `pub type System = pezframe_system`.
pub name: Ident,
/// The path of the pallet, e.g. `pezframe_system` in `pub type System = pezframe_system`.
pub path: syn::Path,
/// The segment of the pallet, e.g. `Pallet` in `pub type System = pezframe_system::Pallet`.
pub pezpallet_segment: Option<syn::PathSegment>,
/// The runtime parameter of the pallet, e.g. `Runtime` in
/// `pub type System = pezframe_system::Pallet<Runtime>`.
pub runtime_param: Option<Ident>,
/// The instance of the pallet, e.g. `Instance1` in `pub type Council =
/// pezpallet_collective<Instance1>`.
pub instance: Option<Ident>,
}
impl PalletDeclaration {
pub fn try_from(
_attr_span: proc_macro2::Span,
item: &syn::ItemType,
path: &syn::Path,
) -> syn::Result<Self> {
let name = item.ident.clone();
let mut path = path.clone();
let mut pezpallet_segment = None;
let mut runtime_param = None;
let mut instance = None;
if let Some(segment) = path.segments.iter_mut().find(|seg| !seg.arguments.is_empty()) {
if let PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
args, ..
}) = segment.arguments.clone()
{
if segment.ident == "Pallet" {
let mut segment = segment.clone();
segment.arguments = PathArguments::None;
pezpallet_segment = Some(segment.clone());
}
let mut args_iter = args.iter();
if let Some(syn::GenericArgument::Type(syn::Type::Path(arg_path))) =
args_iter.next()
{
let ident = arg_path.path.require_ident()?.clone();
if segment.ident == "Pallet" {
runtime_param = Some(ident);
if let Some(syn::GenericArgument::Type(syn::Type::Path(arg_path))) =
args_iter.next()
{
instance = Some(arg_path.path.require_ident()?.clone());
}
} else {
instance = Some(ident);
segment.arguments = PathArguments::None;
}
}
}
}
if pezpallet_segment.is_some() {
path = syn::Path {
leading_colon: None,
segments: path
.segments
.iter()
.filter(|seg| seg.arguments.is_empty())
.cloned()
.collect(),
};
}
Ok(Self { name, path, pezpallet_segment, runtime_param, instance })
}
}
#[test]
fn declaration_works() {
use syn::parse_quote;
let decl: PalletDeclaration = PalletDeclaration::try_from(
proc_macro2::Span::call_site(),
&parse_quote! { pub type System = pezframe_system; },
&parse_quote! { pezframe_system },
)
.expect("Failed to parse pallet declaration");
assert_eq!(decl.name, "System");
assert_eq!(decl.path, parse_quote! { pezframe_system });
assert_eq!(decl.pezpallet_segment, None);
assert_eq!(decl.runtime_param, None);
assert_eq!(decl.instance, None);
}
#[test]
fn declaration_works_with_instance() {
use syn::parse_quote;
let decl: PalletDeclaration = PalletDeclaration::try_from(
proc_macro2::Span::call_site(),
&parse_quote! { pub type System = pezframe_system<Instance1>; },
&parse_quote! { pezframe_system<Instance1> },
)
.expect("Failed to parse pallet declaration");
assert_eq!(decl.name, "System");
assert_eq!(decl.path, parse_quote! { pezframe_system });
assert_eq!(decl.pezpallet_segment, None);
assert_eq!(decl.runtime_param, None);
assert_eq!(decl.instance, Some(parse_quote! { Instance1 }));
}
#[test]
fn declaration_works_with_pallet() {
use syn::parse_quote;
let decl: PalletDeclaration = PalletDeclaration::try_from(
proc_macro2::Span::call_site(),
&parse_quote! { pub type System = pezframe_system::Pallet<Runtime>; },
&parse_quote! { pezframe_system::Pallet<Runtime> },
)
.expect("Failed to parse pallet declaration");
assert_eq!(decl.name, "System");
assert_eq!(decl.path, parse_quote! { pezframe_system });
let segment: syn::PathSegment =
syn::PathSegment { ident: parse_quote! { Pallet }, arguments: PathArguments::None };
assert_eq!(decl.pezpallet_segment, Some(segment));
assert_eq!(decl.runtime_param, Some(parse_quote! { Runtime }));
assert_eq!(decl.instance, None);
}
#[test]
fn declaration_works_with_pallet_and_instance() {
use syn::parse_quote;
let decl: PalletDeclaration = PalletDeclaration::try_from(
proc_macro2::Span::call_site(),
&parse_quote! { pub type System = pezframe_system::Pallet<Runtime, Instance1>; },
&parse_quote! { pezframe_system::Pallet<Runtime, Instance1> },
)
.expect("Failed to parse pallet declaration");
assert_eq!(decl.name, "System");
assert_eq!(decl.path, parse_quote! { pezframe_system });
let segment: syn::PathSegment =
syn::PathSegment { ident: parse_quote! { Pallet }, arguments: PathArguments::None };
assert_eq!(decl.pezpallet_segment, Some(segment));
assert_eq!(decl.runtime_param, Some(parse_quote! { Runtime }));
assert_eq!(decl.instance, Some(parse_quote! { Instance1 }));
}
@@ -0,0 +1,34 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use syn::spanned::Spanned;
pub struct RuntimeStructDef {
pub ident: syn::Ident,
}
impl RuntimeStructDef {
pub fn try_from(item: &mut syn::Item) -> syn::Result<Self> {
let item = if let syn::Item::Struct(item) = item {
item
} else {
let msg = "Invalid runtime::runtime, expected struct definition";
return Err(syn::Error::new(item.span(), msg));
};
Ok(Self { ident: item.ident.clone() })
}
}
@@ -0,0 +1,80 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use syn::{
parse::{Parse, ParseStream},
Result,
};
mod keyword {
use syn::custom_keyword;
custom_keyword!(RuntimeCall);
custom_keyword!(RuntimeEvent);
custom_keyword!(RuntimeError);
custom_keyword!(RuntimeOrigin);
custom_keyword!(RuntimeFreezeReason);
custom_keyword!(RuntimeHoldReason);
custom_keyword!(RuntimeSlashReason);
custom_keyword!(RuntimeLockId);
custom_keyword!(RuntimeTask);
custom_keyword!(RuntimeViewFunction);
}
#[derive(Debug, Clone, PartialEq)]
pub enum RuntimeType {
RuntimeCall(keyword::RuntimeCall),
RuntimeEvent(keyword::RuntimeEvent),
RuntimeError(keyword::RuntimeError),
RuntimeOrigin(keyword::RuntimeOrigin),
RuntimeFreezeReason(keyword::RuntimeFreezeReason),
RuntimeHoldReason(keyword::RuntimeHoldReason),
RuntimeSlashReason(keyword::RuntimeSlashReason),
RuntimeLockId(keyword::RuntimeLockId),
RuntimeTask(keyword::RuntimeTask),
RuntimeViewFunction(keyword::RuntimeViewFunction),
}
impl Parse for RuntimeType {
fn parse(input: ParseStream) -> Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(keyword::RuntimeCall) {
Ok(Self::RuntimeCall(input.parse()?))
} else if lookahead.peek(keyword::RuntimeEvent) {
Ok(Self::RuntimeEvent(input.parse()?))
} else if lookahead.peek(keyword::RuntimeError) {
Ok(Self::RuntimeError(input.parse()?))
} else if lookahead.peek(keyword::RuntimeOrigin) {
Ok(Self::RuntimeOrigin(input.parse()?))
} else if lookahead.peek(keyword::RuntimeFreezeReason) {
Ok(Self::RuntimeFreezeReason(input.parse()?))
} else if lookahead.peek(keyword::RuntimeHoldReason) {
Ok(Self::RuntimeHoldReason(input.parse()?))
} else if lookahead.peek(keyword::RuntimeSlashReason) {
Ok(Self::RuntimeSlashReason(input.parse()?))
} else if lookahead.peek(keyword::RuntimeLockId) {
Ok(Self::RuntimeLockId(input.parse()?))
} else if lookahead.peek(keyword::RuntimeTask) {
Ok(Self::RuntimeTask(input.parse()?))
} else if lookahead.peek(keyword::RuntimeViewFunction) {
Ok(Self::RuntimeViewFunction(input.parse()?))
} else {
Err(lookahead.error())
}
}
}
@@ -0,0 +1,676 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Implementation of the `storage_alias` attribute macro.
use crate::{counter_prefix, pallet::parse::helper};
use pezframe_support_procedural_tools::generate_access_from_frame_or_crate;
use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens};
use syn::{
parenthesized,
parse::{Parse, ParseStream},
punctuated::Punctuated,
spanned::Spanned,
token,
visit::Visit,
Attribute, Error, Ident, Result, Token, Type, TypeParam, Visibility, WhereClause,
};
/// Extension trait for [`Type`].
trait TypeExt {
fn get_ident(&self) -> Option<&Ident>;
fn contains_ident(&self, ident: &Ident) -> bool;
}
impl TypeExt for Type {
fn get_ident(&self) -> Option<&Ident> {
match self {
Type::Path(p) => match &p.qself {
Some(qself) => qself.ty.get_ident(),
None => p.path.get_ident(),
},
_ => None,
}
}
fn contains_ident(&self, ident: &Ident) -> bool {
struct ContainsIdent<'a> {
ident: &'a Ident,
found: bool,
}
impl<'a, 'ast> Visit<'ast> for ContainsIdent<'a> {
fn visit_ident(&mut self, i: &'ast Ident) {
if i == self.ident {
self.found = true;
}
}
}
let mut visitor = ContainsIdent { ident, found: false };
syn::visit::visit_type(&mut visitor, self);
visitor.found
}
}
/// Represents generics which only support [`TypeParam`] separated by commas.
struct SimpleGenerics {
lt_token: Token![<],
params: Punctuated<TypeParam, token::Comma>,
gt_token: Token![>],
}
impl SimpleGenerics {
/// Returns the generics for types declarations etc.
fn type_generics(&self) -> impl Iterator<Item = &Ident> {
self.params.iter().map(|p| &p.ident)
}
/// Returns the generics for the `impl` block.
fn impl_generics(&self) -> impl Iterator<Item = &TypeParam> {
self.params.iter()
}
}
impl Parse for SimpleGenerics {
fn parse(input: ParseStream<'_>) -> Result<Self> {
Ok(Self {
lt_token: input.parse()?,
params: Punctuated::parse_separated_nonempty(input)?,
gt_token: input.parse()?,
})
}
}
impl ToTokens for SimpleGenerics {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.lt_token.to_tokens(tokens);
self.params.to_tokens(tokens);
self.gt_token.to_tokens(tokens);
}
}
mod storage_types {
syn::custom_keyword!(StorageValue);
syn::custom_keyword!(StorageMap);
syn::custom_keyword!(CountedStorageMap);
syn::custom_keyword!(StorageDoubleMap);
syn::custom_keyword!(StorageNMap);
}
/// The types of prefixes the storage alias macro supports.
mod prefix_types {
// Use the verbatim/unmodified input name as the prefix.
syn::custom_keyword!(verbatim);
// The input type is a pallet and its pallet name should be used as the prefix.
syn::custom_keyword!(pezpallet_name);
// The input type implements `Get<'static str>` and this `str` should be used as the prefix.
syn::custom_keyword!(dynamic);
}
/// The supported storage types
enum StorageType {
Value {
_kw: storage_types::StorageValue,
_lt_token: Token![<],
prefix: Type,
_value_comma: Token![,],
value_ty: Type,
query_type: Option<(Token![,], Type)>,
_trailing_comma: Option<Token![,]>,
_gt_token: Token![>],
},
Map {
_kw: storage_types::StorageMap,
_lt_token: Token![<],
prefix: Type,
_hasher_comma: Token![,],
hasher_ty: Type,
_key_comma: Token![,],
key_ty: Type,
_value_comma: Token![,],
value_ty: Type,
query_type: Option<(Token![,], Type)>,
_trailing_comma: Option<Token![,]>,
_gt_token: Token![>],
},
CountedMap {
_kw: storage_types::CountedStorageMap,
_lt_token: Token![<],
prefix: Type,
_hasher_comma: Token![,],
hasher_ty: Type,
_key_comma: Token![,],
key_ty: Type,
_value_comma: Token![,],
value_ty: Type,
query_type: Option<(Token![,], Type)>,
_trailing_comma: Option<Token![,]>,
_gt_token: Token![>],
},
DoubleMap {
_kw: storage_types::StorageDoubleMap,
_lt_token: Token![<],
prefix: Type,
_hasher1_comma: Token![,],
hasher1_ty: Type,
_key1_comma: Token![,],
key1_ty: Type,
_hasher2_comma: Token![,],
hasher2_ty: Type,
_key2_comma: Token![,],
key2_ty: Type,
_value_comma: Token![,],
value_ty: Type,
query_type: Option<(Token![,], Type)>,
_trailing_comma: Option<Token![,]>,
_gt_token: Token![>],
},
NMap {
_kw: storage_types::StorageNMap,
_lt_token: Token![<],
prefix: Type,
_paren_comma: Token![,],
_paren_token: token::Paren,
key_types: Punctuated<Type, Token![,]>,
_value_comma: Token![,],
value_ty: Type,
query_type: Option<(Token![,], Type)>,
_trailing_comma: Option<Token![,]>,
_gt_token: Token![>],
},
}
impl StorageType {
/// Generate the actual type declaration.
fn generate_type_declaration(
&self,
crate_: &syn::Path,
storage_instance: &StorageInstance,
storage_name: &Ident,
storage_generics: Option<&SimpleGenerics>,
visibility: &Visibility,
attributes: &[Attribute],
) -> TokenStream {
let storage_instance_generics = &storage_instance.generics;
let storage_instance = &storage_instance.name;
let attributes = attributes.iter();
let storage_generics = storage_generics.map(|g| {
let generics = g.type_generics();
quote!( < #( #generics ),* > )
});
match self {
Self::Value { value_ty, query_type, .. } => {
let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t));
quote! {
#( #attributes )*
#visibility type #storage_name #storage_generics = #crate_::storage::types::StorageValue<
#storage_instance #storage_instance_generics,
#value_ty
#query_type
>;
}
},
Self::CountedMap { value_ty, query_type, hasher_ty, key_ty, .. } |
Self::Map { value_ty, query_type, hasher_ty, key_ty, .. } => {
let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t));
let map_type = Ident::new(
match self {
Self::Map { .. } => "StorageMap",
_ => "CountedStorageMap",
},
Span::call_site(),
);
quote! {
#( #attributes )*
#visibility type #storage_name #storage_generics = #crate_::storage::types::#map_type<
#storage_instance #storage_instance_generics,
#hasher_ty,
#key_ty,
#value_ty
#query_type
>;
}
},
Self::DoubleMap {
value_ty,
query_type,
hasher1_ty,
key1_ty,
hasher2_ty,
key2_ty,
..
} => {
let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t));
quote! {
#( #attributes )*
#visibility type #storage_name #storage_generics = #crate_::storage::types::StorageDoubleMap<
#storage_instance #storage_instance_generics,
#hasher1_ty,
#key1_ty,
#hasher2_ty,
#key2_ty,
#value_ty
#query_type
>;
}
},
Self::NMap { value_ty, query_type, key_types, .. } => {
let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t));
let key_types = key_types.iter();
quote! {
#( #attributes )*
#visibility type #storage_name #storage_generics = #crate_::storage::types::StorageNMap<
#storage_instance #storage_instance_generics,
( #( #key_types ),* ),
#value_ty
#query_type
>;
}
},
}
}
/// The prefix for this storage type.
fn prefix(&self) -> &Type {
match self {
Self::Value { prefix, .. } |
Self::Map { prefix, .. } |
Self::CountedMap { prefix, .. } |
Self::NMap { prefix, .. } |
Self::DoubleMap { prefix, .. } => prefix,
}
}
}
impl Parse for StorageType {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let lookahead = input.lookahead1();
let parse_query_type = |input: ParseStream<'_>| -> Result<Option<(Token![,], Type)>> {
if input.peek(Token![,]) && !input.peek2(Token![>]) {
Ok(Some((input.parse()?, input.parse()?)))
} else {
Ok(None)
}
};
if lookahead.peek(storage_types::StorageValue) {
Ok(Self::Value {
_kw: input.parse()?,
_lt_token: input.parse()?,
prefix: input.parse()?,
_value_comma: input.parse()?,
value_ty: input.parse()?,
query_type: parse_query_type(input)?,
_trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?,
_gt_token: input.parse()?,
})
} else if lookahead.peek(storage_types::StorageMap) {
Ok(Self::Map {
_kw: input.parse()?,
_lt_token: input.parse()?,
prefix: input.parse()?,
_hasher_comma: input.parse()?,
hasher_ty: input.parse()?,
_key_comma: input.parse()?,
key_ty: input.parse()?,
_value_comma: input.parse()?,
value_ty: input.parse()?,
query_type: parse_query_type(input)?,
_trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?,
_gt_token: input.parse()?,
})
} else if lookahead.peek(storage_types::CountedStorageMap) {
Ok(Self::CountedMap {
_kw: input.parse()?,
_lt_token: input.parse()?,
prefix: input.parse()?,
_hasher_comma: input.parse()?,
hasher_ty: input.parse()?,
_key_comma: input.parse()?,
key_ty: input.parse()?,
_value_comma: input.parse()?,
value_ty: input.parse()?,
query_type: parse_query_type(input)?,
_trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?,
_gt_token: input.parse()?,
})
} else if lookahead.peek(storage_types::StorageDoubleMap) {
Ok(Self::DoubleMap {
_kw: input.parse()?,
_lt_token: input.parse()?,
prefix: input.parse()?,
_hasher1_comma: input.parse()?,
hasher1_ty: input.parse()?,
_key1_comma: input.parse()?,
key1_ty: input.parse()?,
_hasher2_comma: input.parse()?,
hasher2_ty: input.parse()?,
_key2_comma: input.parse()?,
key2_ty: input.parse()?,
_value_comma: input.parse()?,
value_ty: input.parse()?,
query_type: parse_query_type(input)?,
_trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?,
_gt_token: input.parse()?,
})
} else if lookahead.peek(storage_types::StorageNMap) {
let content;
Ok(Self::NMap {
_kw: input.parse()?,
_lt_token: input.parse()?,
prefix: input.parse()?,
_paren_comma: input.parse()?,
_paren_token: parenthesized!(content in input),
key_types: Punctuated::parse_terminated(&content)?,
_value_comma: input.parse()?,
value_ty: input.parse()?,
query_type: parse_query_type(input)?,
_trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?,
_gt_token: input.parse()?,
})
} else {
Err(lookahead.error())
}
}
}
/// The input expected by this macro.
struct Input {
attributes: Vec<Attribute>,
visibility: Visibility,
_type: Token![type],
storage_name: Ident,
storage_generics: Option<SimpleGenerics>,
where_clause: Option<WhereClause>,
_equal: Token![=],
storage_type: StorageType,
_semicolon: Token![;],
}
impl Parse for Input {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let attributes = input.call(Attribute::parse_outer)?;
let visibility = input.parse()?;
let _type = input.parse()?;
let storage_name = input.parse()?;
let lookahead = input.lookahead1();
let storage_generics = if lookahead.peek(Token![<]) {
Some(input.parse()?)
} else if lookahead.peek(Token![=]) {
None
} else {
return Err(lookahead.error());
};
let lookahead = input.lookahead1();
let where_clause = if lookahead.peek(Token![where]) {
Some(input.parse()?)
} else if lookahead.peek(Token![=]) {
None
} else {
return Err(lookahead.error());
};
let _equal = input.parse()?;
let storage_type = input.parse()?;
let _semicolon = input.parse()?;
Ok(Self {
attributes,
visibility,
_type,
storage_name,
storage_generics,
_equal,
storage_type,
where_clause,
_semicolon,
})
}
}
/// Defines which type of prefix the storage alias is using.
#[derive(Clone, Copy)]
enum PrefixType {
/// An appropriate prefix will be determined automatically.
///
/// If generics are passed, this is assumed to be a pallet and the pallet name should be used.
/// Otherwise use the verbatim passed name as prefix.
Compatibility,
/// The provided ident/name will be used as the prefix.
Verbatim,
/// The provided type will be used to determine the prefix. This type must
/// implement `PalletInfoAccess` which specifies the proper name. This
/// name is then used as the prefix.
PalletName,
/// Uses the provided type implementing `Get<'static str>` to determine the prefix.
Dynamic,
}
/// Implementation of the `storage_alias` attribute macro.
pub fn storage_alias(attributes: TokenStream, input: TokenStream) -> Result<TokenStream> {
let input = syn::parse2::<Input>(input)?;
let crate_ = generate_access_from_frame_or_crate("pezframe-support")?;
let prefix_type = if attributes.is_empty() {
PrefixType::Compatibility
} else if syn::parse2::<prefix_types::verbatim>(attributes.clone()).is_ok() {
PrefixType::Verbatim
} else if syn::parse2::<prefix_types::pezpallet_name>(attributes.clone()).is_ok() {
PrefixType::PalletName
} else if syn::parse2::<prefix_types::dynamic>(attributes.clone()).is_ok() {
PrefixType::Dynamic
} else {
return Err(Error::new(attributes.span(), "Unknown attributes"));
};
let storage_instance = generate_storage_instance(
&crate_,
&input.storage_name,
input.storage_generics.as_ref(),
input.where_clause.as_ref(),
input.storage_type.prefix(),
&input.visibility,
matches!(input.storage_type, StorageType::CountedMap { .. }),
prefix_type,
)?;
let definition = input.storage_type.generate_type_declaration(
&crate_,
&storage_instance,
&input.storage_name,
input.storage_generics.as_ref(),
&input.visibility,
&input.attributes,
);
let storage_instance_code = storage_instance.code;
Ok(quote! {
#storage_instance_code
#definition
})
}
/// The storage instance to use for the storage alias.
struct StorageInstance {
name: Ident,
generics: TokenStream,
code: TokenStream,
}
/// Generate the [`StorageInstance`] for the storage alias.
fn generate_storage_instance(
crate_: &syn::Path,
storage_name: &Ident,
storage_generics: Option<&SimpleGenerics>,
storage_where_clause: Option<&WhereClause>,
prefix: &Type,
visibility: &Visibility,
is_counted_map: bool,
prefix_type: PrefixType,
) -> Result<StorageInstance> {
if let Type::Infer(_) = prefix {
return Err(Error::new(prefix.span(), "`_` is not allowed as prefix by `storage_alias`."));
}
let impl_generics_used_by_prefix = storage_generics
.as_ref()
.map(|g| {
g.impl_generics()
.filter(|g| prefix.contains_ident(&g.ident))
.collect::<Vec<_>>()
})
.unwrap_or_default();
let (pezpallet_prefix, impl_generics, type_generics) = match prefix_type {
PrefixType::Compatibility =>
if !impl_generics_used_by_prefix.is_empty() {
let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident);
let impl_generics = impl_generics_used_by_prefix.iter();
(
quote! {
< #prefix as #crate_::traits::PalletInfoAccess>::name()
},
quote!( #( #impl_generics ),* ),
quote!( #( #type_generics ),* ),
)
} else if let Some(prefix) = prefix.get_ident() {
let prefix_str = prefix.to_string();
(quote!(#prefix_str), quote!(), quote!())
} else {
return Err(Error::new_spanned(
prefix,
"If there are no generics, the prefix is only allowed to be an identifier.",
));
},
PrefixType::Verbatim => {
let prefix_str = match prefix.get_ident() {
Some(p) => p.to_string(),
None =>
return Err(Error::new_spanned(
prefix,
"Prefix type `verbatim` requires that the prefix is an ident.",
)),
};
(quote!(#prefix_str), quote!(), quote!())
},
PrefixType::PalletName => {
let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident);
let impl_generics = impl_generics_used_by_prefix.iter();
(
quote! {
<#prefix as #crate_::traits::PalletInfoAccess>::name()
},
quote!( #( #impl_generics ),* ),
quote!( #( #type_generics ),* ),
)
},
PrefixType::Dynamic => {
let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident);
let impl_generics = impl_generics_used_by_prefix.iter();
(
quote! {
<#prefix as #crate_::traits::Get<_>>::get()
},
quote!( #( #impl_generics ),* ),
quote!( #( #type_generics ),* ),
)
},
};
let where_clause = storage_where_clause.map(|w| quote!(#w)).unwrap_or_default();
let name_str = format!("{}_Storage_Instance", storage_name);
let name = Ident::new(&name_str, Span::call_site());
let storage_name_str = storage_name.to_string();
let counter_code = is_counted_map.then(|| {
let counter_name = Ident::new(&counter_prefix(&name_str), Span::call_site());
let counter_storage_name_str = counter_prefix(&storage_name_str);
let storage_prefix_hash = helper::two128_str(&counter_storage_name_str);
quote! {
#visibility struct #counter_name< #impl_generics >(
::core::marker::PhantomData<(#type_generics)>
) #where_clause;
impl<#impl_generics> #crate_::traits::StorageInstance
for #counter_name< #type_generics > #where_clause
{
fn pezpallet_prefix() -> &'static str {
#pezpallet_prefix
}
const STORAGE_PREFIX: &'static str = #counter_storage_name_str;
fn storage_prefix_hash() -> [u8; 16] {
#storage_prefix_hash
}
}
impl<#impl_generics> #crate_::storage::types::CountedStorageMapInstance
for #name< #type_generics > #where_clause
{
type CounterPrefix = #counter_name < #type_generics >;
}
}
});
let storage_prefix_hash = helper::two128_str(&storage_name_str);
// Implement `StorageInstance` trait.
let code = quote! {
#[allow(non_camel_case_types)]
#visibility struct #name< #impl_generics >(
::core::marker::PhantomData<(#type_generics)>
) #where_clause;
impl<#impl_generics> #crate_::traits::StorageInstance
for #name< #type_generics > #where_clause
{
fn pezpallet_prefix() -> &'static str {
#pezpallet_prefix
}
const STORAGE_PREFIX: &'static str = #storage_name_str;
fn storage_prefix_hash() -> [u8; 16] {
#storage_prefix_hash
}
}
#counter_code
};
Ok(StorageInstance { name, code, generics: quote!( < #type_generics > ) })
}
@@ -0,0 +1,60 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use pezframe_support_procedural_tools::generate_access_from_frame_or_crate;
use proc_macro::TokenStream;
use quote::quote;
use syn::{ItemFn, Result};
pub fn transactional(_attr: TokenStream, input: TokenStream) -> Result<TokenStream> {
let ItemFn { attrs, vis, sig, block } = syn::parse(input)?;
let crate_ = generate_access_from_frame_or_crate("pezframe-support")?;
let output = quote! {
#(#attrs)*
#vis #sig {
use #crate_::storage::{with_transaction, TransactionOutcome};
with_transaction(|| {
let r = (|| { #block })();
if r.is_ok() {
TransactionOutcome::Commit(r)
} else {
TransactionOutcome::Rollback(r)
}
})
}
};
Ok(output.into())
}
pub fn require_transactional(_attr: TokenStream, input: TokenStream) -> Result<TokenStream> {
let ItemFn { attrs, vis, sig, block } = syn::parse(input)?;
let crate_ = generate_access_from_frame_or_crate("pezframe-support")?;
let output = quote! {
#(#attrs)*
#vis #sig {
if !#crate_::storage::transactional::is_transactional() {
return Err(#crate_::pezsp_runtime::TransactionalError::NoLayer.into());
}
#block
}
};
Ok(output.into())
}
@@ -0,0 +1,105 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Implementation of the `create_tt_return_macro` macro
use crate::COUNTER;
use proc_macro2::{Ident, TokenStream};
use quote::format_ident;
struct CreateTtReturnMacroDef {
name: Ident,
args: Vec<(Ident, TokenStream)>,
}
impl syn::parse::Parse for CreateTtReturnMacroDef {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let name = input.parse()?;
input.parse::<syn::Token![,]>()?;
let mut args = Vec::new();
while !input.is_empty() {
let mut value;
let key: Ident = input.parse()?;
input.parse::<syn::Token![=]>()?;
let _: syn::token::Bracket = syn::bracketed!(value in input);
let _: syn::token::Brace = syn::braced!(value in value);
let value: TokenStream = value.parse()?;
args.push((key, value))
}
Ok(Self { name, args })
}
}
/// A proc macro that accepts a name and any number of key-value pairs, to be used to create a
/// declarative macro that follows tt-call conventions and simply calls
/// [`tt_call::tt_return`], accepting an optional `pezframe-support` argument and returning
/// the key-value pairs that were supplied to the proc macro.
///
/// # Example
/// ```ignore
/// __create_tt_macro! {
/// my_tt_macro,
/// foo = [{ bar }]
/// }
///
/// // Creates the following declarative macro:
///
/// macro_rules! my_tt_macro {
/// {
/// $caller:tt
/// $(your_tt_return = [{ $my_tt_return:path }])?
/// } => {
/// $my_tt_return! {
/// $caller
/// foo = [{ bar }]
/// }
/// }
/// }
/// ```
pub fn create_tt_return_macro(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let CreateTtReturnMacroDef { name, args } =
syn::parse_macro_input!(input as CreateTtReturnMacroDef);
let (keys, values): (Vec<_>, Vec<_>) = args.into_iter().unzip();
let count = COUNTER.with(|counter| counter.borrow_mut().inc());
let unique_name = format_ident!("{}_{}", name, count);
let decl_macro = quote::quote! {
#[macro_export]
#[doc(hidden)]
macro_rules! #unique_name {
{
$caller:tt
$(your_tt_return = [{ $my_tt_macro:path }])?
} => {
$my_tt_return! {
$caller
#(
#keys = [{ #values }]
)*
}
}
}
pub use #unique_name as #name;
};
decl_macro.into()
}
@@ -0,0 +1,22 @@
[package]
name = "pezframe-support-procedural-tools"
version = "10.0.0"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
homepage.workspace = true
repository.workspace = true
description = "Proc macro helpers for procedural macros"
[lints]
workspace = true
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
pezframe-support-procedural-tools-derive = { workspace = true, default-features = true }
proc-macro-crate = { workspace = true }
proc-macro2 = { workspace = true }
quote = { workspace = true }
syn = { features = ["extra-traits", "full", "visit"], workspace = true }
@@ -0,0 +1,28 @@
[package]
name = "pezframe-support-procedural-tools-derive"
version = "11.0.0"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
homepage.workspace = true
repository.workspace = true
description = "Use to derive parsing for parsing struct."
[lints]
workspace = true
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[lib]
proc-macro = true
[dependencies]
proc-macro2 = { workspace = true }
quote = { features = ["proc-macro"], workspace = true }
syn = { features = [
"extra-traits",
"full",
"parsing",
"proc-macro",
], workspace = true }
@@ -0,0 +1,152 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// tag::description[]
//! Use to derive parsing for parsing struct.
// end::description[]
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use syn::parse_macro_input;
pub(crate) fn fields_idents(
fields: impl Iterator<Item = syn::Field>,
) -> impl Iterator<Item = proc_macro2::TokenStream> {
fields.enumerate().map(|(ix, field)| {
field.ident.map(|i| quote! {#i}).unwrap_or_else(|| {
let f_ix: syn::Ident = syn::Ident::new(&format!("f_{}", ix), Span::call_site());
quote!( #f_ix )
})
})
}
pub(crate) fn fields_access(
fields: impl Iterator<Item = syn::Field>,
) -> impl Iterator<Item = proc_macro2::TokenStream> {
fields.enumerate().map(|(ix, field)| {
field.ident.map(|i| quote!( #i )).unwrap_or_else(|| {
let f_ix: syn::Index = syn::Index { index: ix as u32, span: Span::call_site() };
quote!( #f_ix )
})
})
}
/// self defined parsing struct.
/// not meant for any struct, just for fast
/// parse implementation.
#[proc_macro_derive(Parse)]
pub fn derive_parse(input: TokenStream) -> TokenStream {
let item = parse_macro_input!(input as syn::Item);
match item {
syn::Item::Struct(input) => derive_parse_struct(input),
_ => TokenStream::new(), // ignore
}
}
fn derive_parse_struct(input: syn::ItemStruct) -> TokenStream {
let syn::ItemStruct { ident, generics, fields, .. } = input;
let field_names = {
let name = fields_idents(fields.iter().map(Clone::clone));
quote! {
#(
#name,
)*
}
};
let field = fields_idents(fields.iter().map(Clone::clone));
let tokens = quote! {
impl #generics syn::parse::Parse for #ident #generics {
fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
#(
let #field = input.parse()?;
)*
Ok(Self {
#field_names
})
}
}
};
tokens.into()
}
/// self defined parsing struct or enum.
/// not meant for any struct/enum, just for fast
/// parse implementation.
/// For enum:
/// it only output fields (empty field act as a None).
#[proc_macro_derive(ToTokens)]
pub fn derive_totokens(input: TokenStream) -> TokenStream {
let item = parse_macro_input!(input as syn::Item);
match item {
syn::Item::Enum(input) => derive_totokens_enum(input),
syn::Item::Struct(input) => derive_totokens_struct(input),
_ => TokenStream::new(), // ignore
}
}
fn derive_totokens_struct(input: syn::ItemStruct) -> TokenStream {
let syn::ItemStruct { ident, generics, fields, .. } = input;
let fields = fields_access(fields.iter().map(Clone::clone));
let tokens = quote! {
impl #generics quote::ToTokens for #ident #generics {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
#(
self.#fields.to_tokens(tokens);
)*
}
}
};
tokens.into()
}
fn derive_totokens_enum(input: syn::ItemEnum) -> TokenStream {
let syn::ItemEnum { ident, generics, variants, .. } = input;
let variants = variants.iter().map(|v| {
let v_ident = v.ident.clone();
let fields_build = if v.fields.iter().count() > 0 {
let fields_id = fields_idents(v.fields.iter().map(Clone::clone));
quote!( (#(#fields_id), *) )
} else {
quote!()
};
let field = fields_idents(v.fields.iter().map(Clone::clone));
quote! {
#ident::#v_ident #fields_build => {
#(
#field.to_tokens(tokens);
)*
},
}
});
let tokens = quote! {
impl #generics quote::ToTokens for #ident #generics {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
match self {
#(
#variants
)*
}
}
}
};
tokens.into()
}
@@ -0,0 +1,197 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// tag::description[]
//! Proc macro helpers for procedural macros
// end::description[]
// reexport proc macros
pub use pezframe_support_procedural_tools_derive::*;
use proc_macro_crate::{crate_name, FoundCrate};
use quote::quote;
use syn::parse::Error;
pub mod syn_ext;
// FIXME #1569, remove the following functions, which are copied from sp-api-macros
use proc_macro2::{Span, TokenStream};
use syn::Ident;
fn generate_hidden_includes_mod_name(unique_id: &str) -> Ident {
Ident::new(&format!("pezsp_api_hidden_includes_{}", unique_id), Span::call_site())
}
/// Generates the access to the `pezframe-support` crate.
pub fn generate_crate_access(unique_id: &str, def_crate: &str) -> TokenStream {
if std::env::var("CARGO_PKG_NAME").unwrap() == def_crate {
let pezframe_support = match generate_access_from_frame_or_crate("pezframe-support") {
Ok(c) => c,
Err(e) => return e.into_compile_error().into(),
};
quote::quote!(#pezframe_support)
} else {
let mod_name = generate_hidden_includes_mod_name(unique_id);
quote::quote!( self::#mod_name::hidden_include )
}
}
/// Check if a path is using the `frame` crate or not.
///
/// This will usually check the output of [`generate_access_from_frame_or_crate`].
/// We want to know if whatever the `path` takes us to, is exported from `frame` or not. In that
/// case `path` would start with `frame`, something like `pezkuwi_sdk_frame::x::y:z` or
/// frame::x::y:z.
pub fn is_using_frame_crate(path: &syn::Path) -> bool {
path.segments
.first()
.map(|s| s.ident == "pezkuwi_sdk_frame" || s.ident == "frame")
.unwrap_or(false)
}
/// Generate the crate access for the crate using 2018 syntax.
///
/// If `frame` is in scope, it will use `pezkuwi_sdk_frame::deps::<def_crate>`. Else, it will try
/// and find `<def_crate>` directly.
pub fn generate_access_from_frame_or_crate(def_crate: &str) -> Result<syn::Path, Error> {
if let Some(path) = get_frame_crate_path(def_crate) {
Ok(path)
} else if let Some(path) = get_sdk_crate_path(def_crate) {
Ok(path)
} else {
let ident = match crate_name(def_crate) {
Ok(FoundCrate::Itself) => {
let name = def_crate.to_string().replace("-", "_");
Ok(syn::Ident::new(&name, Span::call_site()))
},
Ok(FoundCrate::Name(name)) => Ok(Ident::new(&name, Span::call_site())),
Err(e) => Err(Error::new(Span::call_site(), e)),
}?;
Ok(syn::Path::from(ident))
}
}
/// Generates the hidden includes that are required to make the macro independent from its scope.
pub fn generate_hidden_includes(unique_id: &str, def_crate: &str) -> TokenStream {
let mod_name = generate_hidden_includes_mod_name(unique_id);
if let Some(path) = get_frame_crate_path(def_crate) {
quote::quote!(
#[doc(hidden)]
mod #mod_name {
pub use #path as hidden_include;
}
)
} else if let Some(path) = get_sdk_crate_path(def_crate) {
quote::quote!(
#[doc(hidden)]
mod #mod_name {
pub use #path as hidden_include;
}
)
} else {
match crate_name(def_crate) {
Ok(FoundCrate::Itself) => quote!(),
Ok(FoundCrate::Name(name)) => {
let name = Ident::new(&name, Span::call_site());
quote::quote!(
#[doc(hidden)]
mod #mod_name {
pub use #name as hidden_include;
}
)
},
Err(e) => {
let err = Error::new(Span::call_site(), e).to_compile_error();
quote!( #err )
},
}
}
}
/// Generates the path to the frame crate deps.
fn get_frame_crate_path(def_crate: &str) -> Option<syn::Path> {
// This does not work if the frame crate is renamed.
if let Ok(FoundCrate::Name(name)) =
crate_name(&"pezkuwi-sdk-frame").or_else(|_| crate_name(&"frame"))
{
let path = format!("{}::deps::{}", name, def_crate.to_string().replace("-", "_"));
Some(syn::parse_str::<syn::Path>(&path).expect("is a valid path; qed"))
} else {
None
}
}
fn get_sdk_crate_path(def_crate: &str) -> Option<syn::Path> {
if let Ok(FoundCrate::Name(name)) = crate_name(&"pezkuwi-sdk") {
let path = format!("{}::{}", name, def_crate.to_string()).replace("-", "_");
Some(syn::parse_str::<syn::Path>(&path).expect("is a valid path; qed"))
} else {
None
}
}
// fn to remove white spaces around string types
// (basically whitespaces around tokens)
pub fn clean_type_string(input: &str) -> String {
input
.replace(" ::", "::")
.replace(":: ", "::")
.replace(" ,", ",")
.replace(" ;", ";")
.replace(" [", "[")
.replace("[ ", "[")
.replace(" ]", "]")
.replace(" (", "(")
.replace("( ", "(")
.replace(" )", ")")
.replace(" <", "<")
.replace("< ", "<")
.replace(" >", ">")
}
/// Return all doc attributes literals found.
pub fn get_doc_literals(attrs: &[syn::Attribute]) -> Vec<syn::Expr> {
attrs
.iter()
.filter_map(|attr| {
if let syn::Meta::NameValue(meta) = &attr.meta {
meta.path
.get_ident()
.filter(|ident| *ident == "doc")
.map(|_| meta.value.clone())
} else {
None
}
})
.collect()
}
/// Return all cfg attributes literals found.
pub fn get_cfg_attributes(attrs: &[syn::Attribute]) -> Vec<syn::Attribute> {
attrs
.iter()
.filter_map(|attr| {
if let syn::Meta::List(meta) = &attr.meta {
meta.path.get_ident().filter(|ident| *ident == "cfg").map(|_| attr.clone())
} else {
None
}
})
.collect()
}
@@ -0,0 +1,230 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// tag::description[]
//! Extension to syn types, mainly for parsing
// end::description[]
use pezframe_support_procedural_tools_derive::{Parse, ToTokens};
use proc_macro2::{TokenStream, TokenTree};
use quote::ToTokens;
use std::iter::once;
use syn::{
parse::{Parse, ParseStream, Result},
visit::{self, Visit},
Ident,
};
/// stop parsing here getting remaining token as content
/// Warn duplicate stream (part of)
#[derive(Parse, ToTokens, Debug)]
pub struct StopParse {
pub inner: TokenStream,
}
// inner macro really dependant on syn naming convention, do not export
macro_rules! groups_impl {
($name:ident, $tok:ident, $deli:ident, $parse:ident) => {
#[derive(Debug)]
pub struct $name<P> {
pub token: syn::token::$tok,
pub content: P,
}
impl<P: Parse> Parse for $name<P> {
fn parse(input: ParseStream) -> Result<Self> {
let content;
let token = syn::$parse!(content in input);
let content = content.parse()?;
Ok($name { token, content })
}
}
impl<P: ToTokens> ToTokens for $name<P> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let mut inner_stream = TokenStream::new();
self.content.to_tokens(&mut inner_stream);
let token_tree: proc_macro2::TokenTree =
proc_macro2::Group::new(proc_macro2::Delimiter::$deli, inner_stream).into();
tokens.extend(once(token_tree));
}
}
impl<P: Clone> Clone for $name<P> {
fn clone(&self) -> Self {
Self { token: self.token.clone(), content: self.content.clone() }
}
}
};
}
groups_impl!(Braces, Brace, Brace, braced);
groups_impl!(Brackets, Bracket, Bracket, bracketed);
groups_impl!(Parens, Paren, Parenthesis, parenthesized);
#[derive(Debug)]
pub struct PunctuatedInner<P, T, V> {
pub inner: syn::punctuated::Punctuated<P, T>,
pub variant: V,
}
#[derive(Debug, Clone)]
pub struct NoTrailing;
#[derive(Debug, Clone)]
pub struct Trailing;
pub type Punctuated<P, T> = PunctuatedInner<P, T, NoTrailing>;
pub type PunctuatedTrailing<P, T> = PunctuatedInner<P, T, Trailing>;
impl<P: Parse, T: Parse + syn::token::Token> Parse for PunctuatedInner<P, T, Trailing> {
fn parse(input: ParseStream) -> Result<Self> {
Ok(PunctuatedInner {
inner: syn::punctuated::Punctuated::parse_separated_nonempty(input)?,
variant: Trailing,
})
}
}
impl<P: Parse, T: Parse> Parse for PunctuatedInner<P, T, NoTrailing> {
fn parse(input: ParseStream) -> Result<Self> {
Ok(PunctuatedInner {
inner: syn::punctuated::Punctuated::parse_terminated(input)?,
variant: NoTrailing,
})
}
}
impl<P: ToTokens, T: ToTokens, V> ToTokens for PunctuatedInner<P, T, V> {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.inner.to_tokens(tokens)
}
}
impl<P: Clone, T: Clone, V: Clone> Clone for PunctuatedInner<P, T, V> {
fn clone(&self) -> Self {
Self { inner: self.inner.clone(), variant: self.variant.clone() }
}
}
/// Note that syn Meta is almost fine for use case (lacks only `ToToken`)
#[derive(Debug, Clone)]
pub struct Meta {
pub inner: syn::Meta,
}
impl Parse for Meta {
fn parse(input: ParseStream) -> Result<Self> {
Ok(Meta { inner: syn::Meta::parse(input)? })
}
}
impl ToTokens for Meta {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self.inner {
syn::Meta::Path(ref path) => path.to_tokens(tokens),
syn::Meta::List(ref l) => l.to_tokens(tokens),
syn::Meta::NameValue(ref n) => n.to_tokens(tokens),
}
}
}
#[derive(Debug)]
pub struct OuterAttributes {
pub inner: Vec<syn::Attribute>,
}
impl Parse for OuterAttributes {
fn parse(input: ParseStream) -> Result<Self> {
let inner = syn::Attribute::parse_outer(input)?;
Ok(OuterAttributes { inner })
}
}
impl ToTokens for OuterAttributes {
fn to_tokens(&self, tokens: &mut TokenStream) {
for att in self.inner.iter() {
att.to_tokens(tokens);
}
}
}
pub fn extract_type_option(typ: &syn::Type) -> Option<syn::Type> {
if let syn::Type::Path(ref path) = typ {
let v = path.path.segments.last()?;
if v.ident == "Option" {
// Option has only one type argument in angle bracket.
if let syn::PathArguments::AngleBracketed(a) = &v.arguments {
if let syn::GenericArgument::Type(typ) = a.args.last()? {
return Some(typ.clone());
}
}
}
}
None
}
/// Auxiliary structure to check if a given `Ident` is contained in an ast.
struct ContainsIdent<'a> {
ident: &'a Ident,
result: bool,
}
impl<'ast> ContainsIdent<'ast> {
fn visit_tokenstream(&mut self, stream: TokenStream) {
stream.into_iter().for_each(|tt| match tt {
TokenTree::Ident(id) => self.visit_ident(&id),
TokenTree::Group(ref group) => self.visit_tokenstream(group.stream()),
_ => {},
})
}
fn visit_ident(&mut self, ident: &Ident) {
if ident == self.ident {
self.result = true;
}
}
}
impl<'ast> Visit<'ast> for ContainsIdent<'ast> {
fn visit_ident(&mut self, input: &'ast Ident) {
self.visit_ident(input);
}
fn visit_macro(&mut self, input: &'ast syn::Macro) {
self.visit_tokenstream(input.tokens.clone());
visit::visit_macro(self, input);
}
}
/// Check if a `Type` contains the given `Ident`.
pub fn type_contains_ident(typ: &syn::Type, ident: &Ident) -> bool {
let mut visit = ContainsIdent { result: false, ident };
visit::visit_type(&mut visit, typ);
visit.result
}
/// Check if a `Expr` contains the given `Ident`.
pub fn expr_contains_ident(expr: &syn::Expr, ident: &Ident) -> bool {
let mut visit = ContainsIdent { result: false, ident };
visit::visit_expr(&mut visit, expr);
visit.result
}