mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-20 23:21:02 +00:00
Tasks: general system for recognizing and executing service work (#1343)
`polkadot-sdk` version of original tasks PR located here: https://github.com/paritytech/substrate/pull/14329 Fixes #206 ## Status - [x] Generic `Task` trait - [x] `RuntimeTask` aggregated enum, compatible with `construct_runtime!` - [x] Casting between `Task` and `RuntimeTask` without needing `dyn` or `Box` - [x] Tasks Example pallet - [x] Runtime tests for Tasks example pallet - [x] Parsing for task-related macros - [x] Retrofit parsing to make macros optional - [x] Expansion for task-related macros - [x] Adds support for args in tasks - [x] Retrofit tasks example pallet to use macros instead of manual syntax - [x] Weights - [x] Cleanup - [x] UI tests - [x] Docs ## Target Syntax Adapted from https://github.com/paritytech/polkadot-sdk/issues/206#issue-1865172283 ```rust // NOTE: this enum is optional and is auto-generated by the other macros if not present #[pallet::task] pub enum Task<T: Config> { AddNumberIntoTotal { i: u32, } } /// Some running total. #[pallet::storage] pub(super) type Total<T: Config<I>, I: 'static = ()> = StorageValue<_, (u32, u32), ValueQuery>; /// Numbers to be added into the total. #[pallet::storage] pub(super) type Numbers<T: Config<I>, I: 'static = ()> = StorageMap<_, Twox64Concat, u32, u32, OptionQuery>; #[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)] 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(()) } } ``` --------- Co-authored-by: Nikhil Gupta <17176722+gupnik@users.noreply.github.com> Co-authored-by: kianenigma <kian@parity.io> Co-authored-by: Nikhil Gupta <> Co-authored-by: Gavin Wood <gavin@parity.io> Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> Co-authored-by: gupnik <nikhilgupta.iitk@gmail.com>
This commit is contained in:
Generated
+19
@@ -5505,6 +5505,7 @@ dependencies = [
|
|||||||
"proc-macro-warning",
|
"proc-macro-warning",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
"regex",
|
||||||
"sp-core-hashing",
|
"sp-core-hashing",
|
||||||
"syn 2.0.39",
|
"syn 2.0.39",
|
||||||
]
|
]
|
||||||
@@ -6879,6 +6880,7 @@ dependencies = [
|
|||||||
"pallet-election-provider-multi-phase",
|
"pallet-election-provider-multi-phase",
|
||||||
"pallet-election-provider-support-benchmarking",
|
"pallet-election-provider-support-benchmarking",
|
||||||
"pallet-elections-phragmen",
|
"pallet-elections-phragmen",
|
||||||
|
"pallet-example-tasks",
|
||||||
"pallet-fast-unstake",
|
"pallet-fast-unstake",
|
||||||
"pallet-glutton",
|
"pallet-glutton",
|
||||||
"pallet-grandpa",
|
"pallet-grandpa",
|
||||||
@@ -9842,6 +9844,22 @@ dependencies = [
|
|||||||
"sp-std 8.0.0",
|
"sp-std 8.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pallet-example-tasks"
|
||||||
|
version = "1.0.0-dev"
|
||||||
|
dependencies = [
|
||||||
|
"frame-benchmarking",
|
||||||
|
"frame-support",
|
||||||
|
"frame-system",
|
||||||
|
"log",
|
||||||
|
"parity-scale-codec",
|
||||||
|
"scale-info",
|
||||||
|
"sp-core",
|
||||||
|
"sp-io",
|
||||||
|
"sp-runtime",
|
||||||
|
"sp-std 8.0.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pallet-examples"
|
name = "pallet-examples"
|
||||||
version = "4.0.0-dev"
|
version = "4.0.0-dev"
|
||||||
@@ -9853,6 +9871,7 @@ dependencies = [
|
|||||||
"pallet-example-kitchensink",
|
"pallet-example-kitchensink",
|
||||||
"pallet-example-offchain-worker",
|
"pallet-example-offchain-worker",
|
||||||
"pallet-example-split",
|
"pallet-example-split",
|
||||||
|
"pallet-example-tasks",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -308,6 +308,7 @@ members = [
|
|||||||
"substrate/frame/examples/kitchensink",
|
"substrate/frame/examples/kitchensink",
|
||||||
"substrate/frame/examples/offchain-worker",
|
"substrate/frame/examples/offchain-worker",
|
||||||
"substrate/frame/examples/split",
|
"substrate/frame/examples/split",
|
||||||
|
"substrate/frame/examples/tasks",
|
||||||
"substrate/frame/executive",
|
"substrate/frame/executive",
|
||||||
"substrate/frame/fast-unstake",
|
"substrate/frame/fast-unstake",
|
||||||
"substrate/frame/glutton",
|
"substrate/frame/glutton",
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate as collator_selection;
|
use crate as collator_selection;
|
||||||
use frame_support::{
|
use frame_support::{
|
||||||
ord_parameter_types, parameter_types,
|
derive_impl, ord_parameter_types, parameter_types,
|
||||||
traits::{ConstBool, ConstU32, ConstU64, FindAuthor, ValidatorRegistration},
|
traits::{ConstBool, ConstU32, ConstU64, FindAuthor, ValidatorRegistration},
|
||||||
PalletId,
|
PalletId,
|
||||||
};
|
};
|
||||||
@@ -50,6 +50,7 @@ parameter_types! {
|
|||||||
pub const SS58Prefix: u8 = 42;
|
pub const SS58Prefix: u8 = 42;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
|
||||||
impl system::Config for Test {
|
impl system::Config for Test {
|
||||||
type BaseCallFilter = frame_support::traits::Everything;
|
type BaseCallFilter = frame_support::traits::Everything;
|
||||||
type BlockWeights = ();
|
type BlockWeights = ();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use frame_support::{parameter_types, traits::Everything};
|
use frame_support::{derive_impl, parameter_types, traits::Everything};
|
||||||
use frame_system as system;
|
use frame_system as system;
|
||||||
use sp_core::H256;
|
use sp_core::H256;
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
@@ -22,6 +22,7 @@ parameter_types! {
|
|||||||
pub const SS58Prefix: u8 = 42;
|
pub const SS58Prefix: u8 = 42;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
|
||||||
impl system::Config for Test {
|
impl system::Config for Test {
|
||||||
type BaseCallFilter = Everything;
|
type BaseCallFilter = Everything;
|
||||||
type BlockWeights = ();
|
type BlockWeights = ();
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
title: Tasks API - A general system for recognizing and executing service work
|
||||||
|
|
||||||
|
doc:
|
||||||
|
- audience: Runtime Dev
|
||||||
|
description: |
|
||||||
|
The Tasks API allows you to define some service work that can be recognized by a script or an off-chain worker.
|
||||||
|
Such a script can then create and submit all such work items at any given time.
|
||||||
|
`#[pallet:tasks_experimental]` provides a convenient way to define such work items. It can be attached to an
|
||||||
|
`impl` block inside a pallet, whose functions can then be annotated by the following attributes:
|
||||||
|
1. `#[pallet::task_list]`: Define an iterator over the available work items for a task
|
||||||
|
2. `#[pallet::task_condition]`: Define the conditions for a given work item to be valid
|
||||||
|
3. `#[pallet::task_weight]`: Define the weight of a given work item
|
||||||
|
4. `#[pallet::task_index]`: Define the index of a given work item
|
||||||
|
Each such function becomes a variant of the autogenerated enum `Task<T>` for this pallet.
|
||||||
|
All such enums are aggregated into a `RuntimeTask` by `construct_runtime`.
|
||||||
|
An example pallet that uses the Tasks API is available at `substrate/frame/example/tasks`.
|
||||||
|
|
||||||
|
migrations:
|
||||||
|
db: []
|
||||||
|
|
||||||
|
runtime: []
|
||||||
|
|
||||||
|
crates:
|
||||||
|
- name: frame-system
|
||||||
|
- name: frame-support
|
||||||
|
- name: frame-support-procedural
|
||||||
|
- name: pallet-example-tasks
|
||||||
|
|
||||||
|
host_functions: []
|
||||||
@@ -80,6 +80,7 @@ pallet-democracy = { path = "../../../frame/democracy", default-features = false
|
|||||||
pallet-election-provider-multi-phase = { path = "../../../frame/election-provider-multi-phase", default-features = false }
|
pallet-election-provider-multi-phase = { path = "../../../frame/election-provider-multi-phase", default-features = false }
|
||||||
pallet-election-provider-support-benchmarking = { path = "../../../frame/election-provider-support/benchmarking", default-features = false, optional = true }
|
pallet-election-provider-support-benchmarking = { path = "../../../frame/election-provider-support/benchmarking", default-features = false, optional = true }
|
||||||
pallet-elections-phragmen = { path = "../../../frame/elections-phragmen", default-features = false }
|
pallet-elections-phragmen = { path = "../../../frame/elections-phragmen", default-features = false }
|
||||||
|
pallet-example-tasks = { path = "../../../frame/examples/tasks", default-features = false }
|
||||||
pallet-fast-unstake = { path = "../../../frame/fast-unstake", default-features = false }
|
pallet-fast-unstake = { path = "../../../frame/fast-unstake", default-features = false }
|
||||||
pallet-nis = { path = "../../../frame/nis", default-features = false }
|
pallet-nis = { path = "../../../frame/nis", default-features = false }
|
||||||
pallet-grandpa = { path = "../../../frame/grandpa", default-features = false }
|
pallet-grandpa = { path = "../../../frame/grandpa", default-features = false }
|
||||||
@@ -177,6 +178,7 @@ std = [
|
|||||||
"pallet-election-provider-multi-phase/std",
|
"pallet-election-provider-multi-phase/std",
|
||||||
"pallet-election-provider-support-benchmarking?/std",
|
"pallet-election-provider-support-benchmarking?/std",
|
||||||
"pallet-elections-phragmen/std",
|
"pallet-elections-phragmen/std",
|
||||||
|
"pallet-example-tasks/std",
|
||||||
"pallet-fast-unstake/std",
|
"pallet-fast-unstake/std",
|
||||||
"pallet-glutton/std",
|
"pallet-glutton/std",
|
||||||
"pallet-grandpa/std",
|
"pallet-grandpa/std",
|
||||||
@@ -279,6 +281,7 @@ runtime-benchmarks = [
|
|||||||
"pallet-election-provider-multi-phase/runtime-benchmarks",
|
"pallet-election-provider-multi-phase/runtime-benchmarks",
|
||||||
"pallet-election-provider-support-benchmarking/runtime-benchmarks",
|
"pallet-election-provider-support-benchmarking/runtime-benchmarks",
|
||||||
"pallet-elections-phragmen/runtime-benchmarks",
|
"pallet-elections-phragmen/runtime-benchmarks",
|
||||||
|
"pallet-example-tasks/runtime-benchmarks",
|
||||||
"pallet-fast-unstake/runtime-benchmarks",
|
"pallet-fast-unstake/runtime-benchmarks",
|
||||||
"pallet-glutton/runtime-benchmarks",
|
"pallet-glutton/runtime-benchmarks",
|
||||||
"pallet-grandpa/runtime-benchmarks",
|
"pallet-grandpa/runtime-benchmarks",
|
||||||
@@ -353,6 +356,7 @@ try-runtime = [
|
|||||||
"pallet-democracy/try-runtime",
|
"pallet-democracy/try-runtime",
|
||||||
"pallet-election-provider-multi-phase/try-runtime",
|
"pallet-election-provider-multi-phase/try-runtime",
|
||||||
"pallet-elections-phragmen/try-runtime",
|
"pallet-elections-phragmen/try-runtime",
|
||||||
|
"pallet-example-tasks/try-runtime",
|
||||||
"pallet-fast-unstake/try-runtime",
|
"pallet-fast-unstake/try-runtime",
|
||||||
"pallet-glutton/try-runtime",
|
"pallet-glutton/try-runtime",
|
||||||
"pallet-grandpa/try-runtime",
|
"pallet-grandpa/try-runtime",
|
||||||
|
|||||||
@@ -304,6 +304,11 @@ impl frame_system::Config for Runtime {
|
|||||||
|
|
||||||
impl pallet_insecure_randomness_collective_flip::Config for Runtime {}
|
impl pallet_insecure_randomness_collective_flip::Config for Runtime {}
|
||||||
|
|
||||||
|
impl pallet_example_tasks::Config for Runtime {
|
||||||
|
type RuntimeTask = RuntimeTask;
|
||||||
|
type WeightInfo = pallet_example_tasks::weights::SubstrateWeight<Runtime>;
|
||||||
|
}
|
||||||
|
|
||||||
impl pallet_utility::Config for Runtime {
|
impl pallet_utility::Config for Runtime {
|
||||||
type RuntimeEvent = RuntimeEvent;
|
type RuntimeEvent = RuntimeEvent;
|
||||||
type RuntimeCall = RuntimeCall;
|
type RuntimeCall = RuntimeCall;
|
||||||
@@ -2135,6 +2140,7 @@ construct_runtime!(
|
|||||||
SafeMode: pallet_safe_mode,
|
SafeMode: pallet_safe_mode,
|
||||||
Statement: pallet_statement,
|
Statement: pallet_statement,
|
||||||
Broker: pallet_broker,
|
Broker: pallet_broker,
|
||||||
|
TasksExample: pallet_example_tasks,
|
||||||
Mixnet: pallet_mixnet,
|
Mixnet: pallet_mixnet,
|
||||||
SkipFeelessPayment: pallet_skip_feeless_payment,
|
SkipFeelessPayment: pallet_skip_feeless_payment,
|
||||||
}
|
}
|
||||||
@@ -2227,6 +2233,7 @@ mod benches {
|
|||||||
[pallet_conviction_voting, ConvictionVoting]
|
[pallet_conviction_voting, ConvictionVoting]
|
||||||
[pallet_contracts, Contracts]
|
[pallet_contracts, Contracts]
|
||||||
[pallet_core_fellowship, CoreFellowship]
|
[pallet_core_fellowship, CoreFellowship]
|
||||||
|
[tasks_example, TasksExample]
|
||||||
[pallet_democracy, Democracy]
|
[pallet_democracy, Democracy]
|
||||||
[pallet_asset_conversion, AssetConversion]
|
[pallet_asset_conversion, AssetConversion]
|
||||||
[pallet_election_provider_multi_phase, ElectionProviderMultiPhase]
|
[pallet_election_provider_multi_phase, ElectionProviderMultiPhase]
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ pallet-example-frame-crate = { path = "frame-crate", default-features = false }
|
|||||||
pallet-example-kitchensink = { path = "kitchensink", default-features = false }
|
pallet-example-kitchensink = { path = "kitchensink", default-features = false }
|
||||||
pallet-example-offchain-worker = { path = "offchain-worker", default-features = false }
|
pallet-example-offchain-worker = { path = "offchain-worker", default-features = false }
|
||||||
pallet-example-split = { path = "split", default-features = false }
|
pallet-example-split = { path = "split", default-features = false }
|
||||||
|
pallet-example-tasks = { path = "tasks", default-features = false }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
@@ -31,6 +32,7 @@ std = [
|
|||||||
"pallet-example-kitchensink/std",
|
"pallet-example-kitchensink/std",
|
||||||
"pallet-example-offchain-worker/std",
|
"pallet-example-offchain-worker/std",
|
||||||
"pallet-example-split/std",
|
"pallet-example-split/std",
|
||||||
|
"pallet-example-tasks/std",
|
||||||
]
|
]
|
||||||
try-runtime = [
|
try-runtime = [
|
||||||
"pallet-default-config-example/try-runtime",
|
"pallet-default-config-example/try-runtime",
|
||||||
@@ -39,4 +41,5 @@ try-runtime = [
|
|||||||
"pallet-example-kitchensink/try-runtime",
|
"pallet-example-kitchensink/try-runtime",
|
||||||
"pallet-example-offchain-worker/try-runtime",
|
"pallet-example-offchain-worker/try-runtime",
|
||||||
"pallet-example-split/try-runtime",
|
"pallet-example-split/try-runtime",
|
||||||
|
"pallet-example-tasks/try-runtime",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -47,6 +47,10 @@ pub mod pallet {
|
|||||||
#[pallet::no_default] // optional. `RuntimeEvent` is automatically excluded as well.
|
#[pallet::no_default] // optional. `RuntimeEvent` is automatically excluded as well.
|
||||||
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
|
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
|
||||||
|
|
||||||
|
/// The overarching task type.
|
||||||
|
#[pallet::no_default]
|
||||||
|
type RuntimeTask: Task;
|
||||||
|
|
||||||
/// An input parameter to this pallet. This value can have a default, because it is not
|
/// An input parameter to this pallet. This value can have a default, because it is not
|
||||||
/// reliant on `frame_system::Config` or the overarching runtime in any way.
|
/// reliant on `frame_system::Config` or the overarching runtime in any way.
|
||||||
type WithDefaultValue: Get<u32>;
|
type WithDefaultValue: Get<u32>;
|
||||||
@@ -193,6 +197,7 @@ pub mod tests {
|
|||||||
impl pallet_default_config_example::Config for Runtime {
|
impl pallet_default_config_example::Config for Runtime {
|
||||||
// These two both cannot have defaults.
|
// These two both cannot have defaults.
|
||||||
type RuntimeEvent = RuntimeEvent;
|
type RuntimeEvent = RuntimeEvent;
|
||||||
|
type RuntimeTask = RuntimeTask;
|
||||||
|
|
||||||
type HasNoDefault = frame_support::traits::ConstU32<1>;
|
type HasNoDefault = frame_support::traits::ConstU32<1>;
|
||||||
type CannotHaveDefault = SomeCall;
|
type CannotHaveDefault = SomeCall;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ license = "MIT-0"
|
|||||||
homepage = "https://substrate.io"
|
homepage = "https://substrate.io"
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
description = "FRAME example kitchensink pallet"
|
description = "FRAME example kitchensink pallet"
|
||||||
|
publish = false
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
targets = ["x86_64-unknown-linux-gnu"]
|
targets = ["x86_64-unknown-linux-gnu"]
|
||||||
|
|||||||
@@ -43,4 +43,6 @@
|
|||||||
//! - [`pallet_example_frame_crate`]: Example pallet showcasing how one can be
|
//! - [`pallet_example_frame_crate`]: Example pallet showcasing how one can be
|
||||||
//! built using only the `frame` umbrella crate.
|
//! built using only the `frame` umbrella crate.
|
||||||
//!
|
//!
|
||||||
|
//! - [`pallet_example_tasks`]: This pallet demonstrates the use of `Tasks` to execute service work.
|
||||||
|
//!
|
||||||
//! **Tip**: Use `cargo doc --package <pallet-name> --open` to view each pallet's documentation.
|
//! **Tip**: Use `cargo doc --package <pallet-name> --open` to view each pallet's documentation.
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
[package]
|
||||||
|
name = "pallet-example-tasks"
|
||||||
|
version = "1.0.0-dev"
|
||||||
|
authors.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
description = "Pallet to demonstrate the usage of Tasks to recongnize and execute service work"
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
targets = ["x86_64-unknown-linux-gnu"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false }
|
||||||
|
log = { version = "0.4.17", default-features = false }
|
||||||
|
scale-info = { version = "2.10.0", default-features = false, features = ["derive"] }
|
||||||
|
|
||||||
|
frame-support = { path = "../../support", default-features = false }
|
||||||
|
frame-system = { path = "../../system", default-features = false }
|
||||||
|
|
||||||
|
sp-io = { path = "../../../primitives/io", default-features = false }
|
||||||
|
sp-runtime = { path = "../../../primitives/runtime", default-features = false }
|
||||||
|
sp-std = { path = "../../../primitives/std", default-features = false }
|
||||||
|
sp-core = { version = "21.0.0", default-features = false, path = "../../../primitives/core" }
|
||||||
|
|
||||||
|
frame-benchmarking = { path = "../../benchmarking", default-features = false, optional = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["std"]
|
||||||
|
std = [
|
||||||
|
"codec/std",
|
||||||
|
"frame-benchmarking?/std",
|
||||||
|
"frame-support/std",
|
||||||
|
"frame-system/std",
|
||||||
|
"log/std",
|
||||||
|
"scale-info/std",
|
||||||
|
"sp-core/std",
|
||||||
|
"sp-io/std",
|
||||||
|
"sp-runtime/std",
|
||||||
|
"sp-std/std",
|
||||||
|
]
|
||||||
|
runtime-benchmarks = [
|
||||||
|
"frame-benchmarking/runtime-benchmarks",
|
||||||
|
"frame-support/runtime-benchmarks",
|
||||||
|
"frame-system/runtime-benchmarks",
|
||||||
|
"sp-runtime/runtime-benchmarks",
|
||||||
|
]
|
||||||
|
try-runtime = [
|
||||||
|
"frame-support/try-runtime",
|
||||||
|
"frame-system/try-runtime",
|
||||||
|
"sp-runtime/try-runtime",
|
||||||
|
]
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//! Benchmarking for `pallet-example-tasks`.
|
||||||
|
|
||||||
|
#![cfg(feature = "runtime-benchmarks")]
|
||||||
|
|
||||||
|
use crate::*;
|
||||||
|
use frame_benchmarking::v2::*;
|
||||||
|
|
||||||
|
#[benchmarks]
|
||||||
|
mod benchmarks {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[benchmark]
|
||||||
|
fn add_number_into_total() {
|
||||||
|
Numbers::<T>::insert(0, 1);
|
||||||
|
|
||||||
|
#[block]
|
||||||
|
{
|
||||||
|
Task::<T>::add_number_into_total(0).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(Numbers::<T>::get(0), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::mock::Runtime);
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//! This pallet demonstrates the use of the `pallet::task` api for service work.
|
||||||
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
|
use frame_support::dispatch::DispatchResult;
|
||||||
|
// Re-export pallet items so that they can be accessed from the crate namespace.
|
||||||
|
pub use pallet::*;
|
||||||
|
|
||||||
|
pub mod mock;
|
||||||
|
pub mod tests;
|
||||||
|
|
||||||
|
#[cfg(feature = "runtime-benchmarks")]
|
||||||
|
mod benchmarking;
|
||||||
|
|
||||||
|
pub mod weights;
|
||||||
|
pub use weights::*;
|
||||||
|
|
||||||
|
#[frame_support::pallet(dev_mode)]
|
||||||
|
pub mod pallet {
|
||||||
|
use super::*;
|
||||||
|
use frame_support::pallet_prelude::*;
|
||||||
|
|
||||||
|
#[pallet::error]
|
||||||
|
pub enum Error<T> {
|
||||||
|
/// The referenced task was not found.
|
||||||
|
NotFound,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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(T::WeightInfo::add_number_into_total())]
|
||||||
|
#[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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_system::Config {
|
||||||
|
type RuntimeTask: frame_support::traits::Task;
|
||||||
|
type WeightInfo: WeightInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pallet::pallet]
|
||||||
|
pub struct Pallet<T>(_);
|
||||||
|
|
||||||
|
/// 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>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//! Mock runtime for `tasks-example` tests.
|
||||||
|
#![cfg(test)]
|
||||||
|
|
||||||
|
use crate::{self as tasks_example};
|
||||||
|
use frame_support::derive_impl;
|
||||||
|
|
||||||
|
pub type AccountId = u32;
|
||||||
|
pub type Balance = u32;
|
||||||
|
|
||||||
|
type Block = frame_system::mocking::MockBlock<Runtime>;
|
||||||
|
frame_support::construct_runtime!(
|
||||||
|
pub struct Runtime {
|
||||||
|
System: frame_system,
|
||||||
|
TasksExample: tasks_example,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
|
||||||
|
impl frame_system::Config for Runtime {
|
||||||
|
type Block = Block;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl tasks_example::Config for Runtime {
|
||||||
|
type RuntimeTask = RuntimeTask;
|
||||||
|
type WeightInfo = ();
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//! Tests for `pallet-example-tasks`.
|
||||||
|
#![cfg(test)]
|
||||||
|
|
||||||
|
use crate::{mock::*, Numbers, Total};
|
||||||
|
use frame_support::{assert_noop, assert_ok, traits::Task};
|
||||||
|
use sp_runtime::BuildStorage;
|
||||||
|
|
||||||
|
// This function basically just builds a genesis storage key/value store according to
|
||||||
|
// our desired mockup.
|
||||||
|
pub fn new_test_ext() -> sp_io::TestExternalities {
|
||||||
|
let t = RuntimeGenesisConfig {
|
||||||
|
// We use default for brevity, but you can configure as desired if needed.
|
||||||
|
system: Default::default(),
|
||||||
|
}
|
||||||
|
.build_storage()
|
||||||
|
.unwrap();
|
||||||
|
t.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn task_enumerate_works() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
Numbers::<Runtime>::insert(0, 1);
|
||||||
|
assert_eq!(crate::pallet::Task::<Runtime>::iter().collect::<Vec<_>>().len(), 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn runtime_task_enumerate_works_via_frame_system_config() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
Numbers::<Runtime>::insert(0, 1);
|
||||||
|
Numbers::<Runtime>::insert(1, 4);
|
||||||
|
assert_eq!(
|
||||||
|
<Runtime as frame_system::Config>::RuntimeTask::iter().collect::<Vec<_>>().len(),
|
||||||
|
2
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn runtime_task_enumerate_works_via_pallet_config() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
Numbers::<Runtime>::insert(1, 4);
|
||||||
|
assert_eq!(
|
||||||
|
<Runtime as crate::pallet::Config>::RuntimeTask::iter()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.len(),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn task_index_works_at_pallet_level() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
assert_eq!(crate::pallet::Task::<Runtime>::AddNumberIntoTotal { i: 2u32 }.task_index(), 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn task_index_works_at_runtime_level() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
assert_eq!(
|
||||||
|
<Runtime as frame_system::Config>::RuntimeTask::TasksExample(crate::pallet::Task::<
|
||||||
|
Runtime,
|
||||||
|
>::AddNumberIntoTotal {
|
||||||
|
i: 1u32
|
||||||
|
})
|
||||||
|
.task_index(),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn task_execution_works() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
System::set_block_number(1);
|
||||||
|
Numbers::<Runtime>::insert(0, 1);
|
||||||
|
Numbers::<Runtime>::insert(1, 4);
|
||||||
|
|
||||||
|
let task =
|
||||||
|
<Runtime as frame_system::Config>::RuntimeTask::TasksExample(crate::pallet::Task::<
|
||||||
|
Runtime,
|
||||||
|
>::AddNumberIntoTotal {
|
||||||
|
i: 1u32,
|
||||||
|
});
|
||||||
|
assert_ok!(System::do_task(RuntimeOrigin::signed(1), task.clone(),));
|
||||||
|
assert_eq!(Numbers::<Runtime>::get(0), Some(1));
|
||||||
|
assert_eq!(Numbers::<Runtime>::get(1), None);
|
||||||
|
assert_eq!(Total::<Runtime>::get(), (1, 4));
|
||||||
|
System::assert_last_event(frame_system::Event::<Runtime>::TaskCompleted { task }.into());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn task_execution_fails_for_invalid_task() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
Numbers::<Runtime>::insert(1, 4);
|
||||||
|
assert_noop!(
|
||||||
|
System::do_task(
|
||||||
|
RuntimeOrigin::signed(1),
|
||||||
|
<Runtime as frame_system::Config>::RuntimeTask::TasksExample(
|
||||||
|
crate::pallet::Task::<Runtime>::AddNumberIntoTotal { i: 0u32 }
|
||||||
|
),
|
||||||
|
),
|
||||||
|
frame_system::Error::<Runtime>::InvalidTask
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
+84
@@ -0,0 +1,84 @@
|
|||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//! Autogenerated weights for `pallet_example_tasks`
|
||||||
|
//!
|
||||||
|
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
|
||||||
|
//! DATE: 2023-06-02, STEPS: `20`, REPEAT: `10`, LOW RANGE: `[]`, HIGH RANGE: `[]`
|
||||||
|
//! WORST CASE MAP SIZE: `1000000`
|
||||||
|
//! HOSTNAME: `MacBook.local`, CPU: `<UNKNOWN>`
|
||||||
|
//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024
|
||||||
|
|
||||||
|
// Executed Command:
|
||||||
|
// ./target/release/node-template
|
||||||
|
// benchmark
|
||||||
|
// pallet
|
||||||
|
// --chain
|
||||||
|
// dev
|
||||||
|
// --pallet
|
||||||
|
// pallet_example_tasks
|
||||||
|
// --extrinsic
|
||||||
|
// *
|
||||||
|
// --steps
|
||||||
|
// 20
|
||||||
|
// --repeat
|
||||||
|
// 10
|
||||||
|
// --output
|
||||||
|
// frame/examples/tasks/src/weights.rs
|
||||||
|
|
||||||
|
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
|
#![allow(unused_parens)]
|
||||||
|
#![allow(unused_imports)]
|
||||||
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
|
use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
/// Weight functions needed for pallet_template.
|
||||||
|
pub trait WeightInfo {
|
||||||
|
fn add_number_into_total() -> Weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Weight functions for `pallet_example_kitchensink`.
|
||||||
|
pub struct SubstrateWeight<T>(PhantomData<T>);
|
||||||
|
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
||||||
|
/// Storage: Kitchensink OtherFoo (r:0 w:1)
|
||||||
|
/// Proof Skipped: Kitchensink OtherFoo (max_values: Some(1), max_size: None, mode: Measured)
|
||||||
|
fn add_number_into_total() -> Weight {
|
||||||
|
// Proof Size summary in bytes:
|
||||||
|
// Measured: `0`
|
||||||
|
// Estimated: `0`
|
||||||
|
// Minimum execution time: 1_000_000 picoseconds.
|
||||||
|
Weight::from_parts(1_000_000, 0)
|
||||||
|
.saturating_add(Weight::from_parts(0, 0))
|
||||||
|
.saturating_add(T::DbWeight::get().writes(1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WeightInfo for () {
|
||||||
|
/// Storage: Kitchensink OtherFoo (r:0 w:1)
|
||||||
|
/// Proof Skipped: Kitchensink OtherFoo (max_values: Some(1), max_size: None, mode: Measured)
|
||||||
|
fn add_number_into_total() -> Weight {
|
||||||
|
// Proof Size summary in bytes:
|
||||||
|
// Measured: `0`
|
||||||
|
// Estimated: `0`
|
||||||
|
// Minimum execution time: 1_000_000 picoseconds.
|
||||||
|
Weight::from_parts(1_000_000, 0)
|
||||||
|
.saturating_add(Weight::from_parts(0, 0))
|
||||||
|
.saturating_add(RocksDbWeight::get().writes(1))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,7 +21,7 @@ use super::*;
|
|||||||
|
|
||||||
use crate as scheduler;
|
use crate as scheduler;
|
||||||
use frame_support::{
|
use frame_support::{
|
||||||
ord_parameter_types, parameter_types,
|
derive_impl, ord_parameter_types, parameter_types,
|
||||||
traits::{
|
traits::{
|
||||||
ConstU32, ConstU64, Contains, EitherOfDiverse, EqualPrivilegeOnly, OnFinalize, OnInitialize,
|
ConstU32, ConstU64, Contains, EitherOfDiverse, EqualPrivilegeOnly, OnFinalize, OnInitialize,
|
||||||
},
|
},
|
||||||
@@ -118,6 +118,8 @@ parameter_types! {
|
|||||||
Weight::from_parts(2_000_000_000_000, u64::MAX),
|
Weight::from_parts(2_000_000_000_000, u64::MAX),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
|
||||||
impl system::Config for Test {
|
impl system::Config for Test {
|
||||||
type BaseCallFilter = BaseFilter;
|
type BaseCallFilter = BaseFilter;
|
||||||
type BlockWeights = BlockWeights;
|
type BlockWeights = BlockWeights;
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ proc-macro-warning = { version = "1.0.0", default-features = false }
|
|||||||
expander = "2.0.0"
|
expander = "2.0.0"
|
||||||
sp-core-hashing = { path = "../../../primitives/core/hashing" }
|
sp-core-hashing = { path = "../../../primitives/core/hashing" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
regex = "1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
std = []
|
std = []
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ mod metadata;
|
|||||||
mod origin;
|
mod origin;
|
||||||
mod outer_enums;
|
mod outer_enums;
|
||||||
mod slash_reason;
|
mod slash_reason;
|
||||||
|
mod task;
|
||||||
mod unsigned;
|
mod unsigned;
|
||||||
|
|
||||||
pub use call::expand_outer_dispatch;
|
pub use call::expand_outer_dispatch;
|
||||||
@@ -38,4 +39,5 @@ pub use metadata::expand_runtime_metadata;
|
|||||||
pub use origin::expand_outer_origin;
|
pub use origin::expand_outer_origin;
|
||||||
pub use outer_enums::{expand_outer_enum, OuterEnumType};
|
pub use outer_enums::{expand_outer_enum, OuterEnumType};
|
||||||
pub use slash_reason::expand_outer_slash_reason;
|
pub use slash_reason::expand_outer_slash_reason;
|
||||||
|
pub use task::expand_outer_task;
|
||||||
pub use unsigned::expand_outer_validate_unsigned;
|
pub use unsigned::expand_outer_validate_unsigned;
|
||||||
|
|||||||
@@ -0,0 +1,131 @@
|
|||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
pallet_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_paths = Vec::new();
|
||||||
|
for decl in pallet_decls {
|
||||||
|
if decl.find_part("Task").is_none() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let variant_name = &decl.name;
|
||||||
|
let path = &decl.path;
|
||||||
|
let index = decl.index;
|
||||||
|
|
||||||
|
from_impls.push(quote! {
|
||||||
|
impl From<#path::Task<#runtime_name>> for RuntimeTask {
|
||||||
|
fn from(hr: #path::Task<#runtime_name>) -> Self {
|
||||||
|
RuntimeTask::#variant_name(hr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryInto<#path::Task<#runtime_name>> for RuntimeTask {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_into(self) -> Result<#path::Task<#runtime_name>, Self::Error> {
|
||||||
|
match self {
|
||||||
|
RuntimeTask::#variant_name(hr) => Ok(hr),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
task_variants.push(quote! {
|
||||||
|
#[codec(index = #index)]
|
||||||
|
#variant_name(#path::Task<#runtime_name>),
|
||||||
|
});
|
||||||
|
|
||||||
|
variant_names.push(quote!(#variant_name));
|
||||||
|
|
||||||
|
task_paths.push(quote!(#path::Task));
|
||||||
|
}
|
||||||
|
|
||||||
|
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::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 {
|
||||||
|
#(RuntimeTask::#variant_names(val) => val.is_valid(),)*
|
||||||
|
_ => unreachable!(#INCOMPLETE_MATCH_QED),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&self) -> Result<(), #scrate::traits::tasks::__private::DispatchError> {
|
||||||
|
match self {
|
||||||
|
#(RuntimeTask::#variant_names(val) => val.run(),)*
|
||||||
|
_ => unreachable!(#INCOMPLETE_MATCH_QED),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn weight(&self) -> #scrate::pallet_prelude::Weight {
|
||||||
|
match self {
|
||||||
|
#(RuntimeTask::#variant_names(val) => val.weight(),)*
|
||||||
|
_ => unreachable!(#INCOMPLETE_MATCH_QED),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn task_index(&self) -> u32 {
|
||||||
|
match self {
|
||||||
|
#(RuntimeTask::#variant_names(val) => val.task_index(),)*
|
||||||
|
_ => unreachable!(#INCOMPLETE_MATCH_QED),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn iter() -> Self::Enumeration {
|
||||||
|
let mut all_tasks = Vec::new();
|
||||||
|
#(all_tasks.extend(#task_paths::iter().map(RuntimeTask::from).collect::<Vec<_>>());)*
|
||||||
|
all_tasks.into_iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#( #from_impls )*
|
||||||
|
};
|
||||||
|
|
||||||
|
output
|
||||||
|
}
|
||||||
@@ -386,6 +386,7 @@ fn construct_runtime_final_expansion(
|
|||||||
let pallet_to_index = decl_pallet_runtime_setup(&name, &pallets, &scrate);
|
let pallet_to_index = decl_pallet_runtime_setup(&name, &pallets, &scrate);
|
||||||
|
|
||||||
let dispatch = expand::expand_outer_dispatch(&name, system_pallet, &pallets, &scrate);
|
let dispatch = expand::expand_outer_dispatch(&name, system_pallet, &pallets, &scrate);
|
||||||
|
let tasks = expand::expand_outer_task(&name, &pallets, &scrate);
|
||||||
let metadata = expand::expand_runtime_metadata(
|
let metadata = expand::expand_runtime_metadata(
|
||||||
&name,
|
&name,
|
||||||
&pallets,
|
&pallets,
|
||||||
@@ -475,6 +476,8 @@ fn construct_runtime_final_expansion(
|
|||||||
|
|
||||||
#dispatch
|
#dispatch
|
||||||
|
|
||||||
|
#tasks
|
||||||
|
|
||||||
#metadata
|
#metadata
|
||||||
|
|
||||||
#outer_config
|
#outer_config
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ mod keyword {
|
|||||||
syn::custom_keyword!(ValidateUnsigned);
|
syn::custom_keyword!(ValidateUnsigned);
|
||||||
syn::custom_keyword!(FreezeReason);
|
syn::custom_keyword!(FreezeReason);
|
||||||
syn::custom_keyword!(HoldReason);
|
syn::custom_keyword!(HoldReason);
|
||||||
|
syn::custom_keyword!(Task);
|
||||||
syn::custom_keyword!(LockId);
|
syn::custom_keyword!(LockId);
|
||||||
syn::custom_keyword!(SlashReason);
|
syn::custom_keyword!(SlashReason);
|
||||||
syn::custom_keyword!(exclude_parts);
|
syn::custom_keyword!(exclude_parts);
|
||||||
@@ -404,6 +405,7 @@ pub enum PalletPartKeyword {
|
|||||||
ValidateUnsigned(keyword::ValidateUnsigned),
|
ValidateUnsigned(keyword::ValidateUnsigned),
|
||||||
FreezeReason(keyword::FreezeReason),
|
FreezeReason(keyword::FreezeReason),
|
||||||
HoldReason(keyword::HoldReason),
|
HoldReason(keyword::HoldReason),
|
||||||
|
Task(keyword::Task),
|
||||||
LockId(keyword::LockId),
|
LockId(keyword::LockId),
|
||||||
SlashReason(keyword::SlashReason),
|
SlashReason(keyword::SlashReason),
|
||||||
}
|
}
|
||||||
@@ -434,6 +436,8 @@ impl Parse for PalletPartKeyword {
|
|||||||
Ok(Self::FreezeReason(input.parse()?))
|
Ok(Self::FreezeReason(input.parse()?))
|
||||||
} else if lookahead.peek(keyword::HoldReason) {
|
} else if lookahead.peek(keyword::HoldReason) {
|
||||||
Ok(Self::HoldReason(input.parse()?))
|
Ok(Self::HoldReason(input.parse()?))
|
||||||
|
} else if lookahead.peek(keyword::Task) {
|
||||||
|
Ok(Self::Task(input.parse()?))
|
||||||
} else if lookahead.peek(keyword::LockId) {
|
} else if lookahead.peek(keyword::LockId) {
|
||||||
Ok(Self::LockId(input.parse()?))
|
Ok(Self::LockId(input.parse()?))
|
||||||
} else if lookahead.peek(keyword::SlashReason) {
|
} else if lookahead.peek(keyword::SlashReason) {
|
||||||
@@ -459,6 +463,7 @@ impl PalletPartKeyword {
|
|||||||
Self::ValidateUnsigned(_) => "ValidateUnsigned",
|
Self::ValidateUnsigned(_) => "ValidateUnsigned",
|
||||||
Self::FreezeReason(_) => "FreezeReason",
|
Self::FreezeReason(_) => "FreezeReason",
|
||||||
Self::HoldReason(_) => "HoldReason",
|
Self::HoldReason(_) => "HoldReason",
|
||||||
|
Self::Task(_) => "Task",
|
||||||
Self::LockId(_) => "LockId",
|
Self::LockId(_) => "LockId",
|
||||||
Self::SlashReason(_) => "SlashReason",
|
Self::SlashReason(_) => "SlashReason",
|
||||||
}
|
}
|
||||||
@@ -471,7 +476,7 @@ impl PalletPartKeyword {
|
|||||||
|
|
||||||
/// Returns the names of all pallet parts that allow to have a generic argument.
|
/// Returns the names of all pallet parts that allow to have a generic argument.
|
||||||
fn all_generic_arg() -> &'static [&'static str] {
|
fn all_generic_arg() -> &'static [&'static str] {
|
||||||
&["Event", "Error", "Origin", "Config"]
|
&["Event", "Error", "Origin", "Config", "Task"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -489,6 +494,7 @@ impl ToTokens for PalletPartKeyword {
|
|||||||
Self::ValidateUnsigned(inner) => inner.to_tokens(tokens),
|
Self::ValidateUnsigned(inner) => inner.to_tokens(tokens),
|
||||||
Self::FreezeReason(inner) => inner.to_tokens(tokens),
|
Self::FreezeReason(inner) => inner.to_tokens(tokens),
|
||||||
Self::HoldReason(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::LockId(inner) => inner.to_tokens(tokens),
|
||||||
Self::SlashReason(inner) => inner.to_tokens(tokens),
|
Self::SlashReason(inner) => inner.to_tokens(tokens),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -646,7 +646,6 @@ pub fn storage_alias(attributes: TokenStream, input: TokenStream) -> TokenStream
|
|||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// where `TestDefaultConfig` was defined and registered as follows:
|
/// where `TestDefaultConfig` was defined and registered as follows:
|
||||||
///
|
|
||||||
/// ```ignore
|
/// ```ignore
|
||||||
/// pub struct TestDefaultConfig;
|
/// pub struct TestDefaultConfig;
|
||||||
///
|
///
|
||||||
@@ -673,7 +672,6 @@ pub fn storage_alias(attributes: TokenStream, input: TokenStream) -> TokenStream
|
|||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// The above call to `derive_impl` would expand to roughly the following:
|
/// The above call to `derive_impl` would expand to roughly the following:
|
||||||
///
|
|
||||||
/// ```ignore
|
/// ```ignore
|
||||||
/// impl frame_system::Config for Test {
|
/// impl frame_system::Config for Test {
|
||||||
/// use frame_system::config_preludes::TestDefaultConfig;
|
/// use frame_system::config_preludes::TestDefaultConfig;
|
||||||
@@ -881,6 +879,7 @@ pub fn inject_runtime_type(_: TokenStream, tokens: TokenStream) -> TokenStream {
|
|||||||
let item = syn::parse_macro_input!(item as TraitItemType);
|
let item = syn::parse_macro_input!(item as TraitItemType);
|
||||||
if item.ident != "RuntimeCall" &&
|
if item.ident != "RuntimeCall" &&
|
||||||
item.ident != "RuntimeEvent" &&
|
item.ident != "RuntimeEvent" &&
|
||||||
|
item.ident != "RuntimeTask" &&
|
||||||
item.ident != "RuntimeOrigin" &&
|
item.ident != "RuntimeOrigin" &&
|
||||||
item.ident != "RuntimeHoldReason" &&
|
item.ident != "RuntimeHoldReason" &&
|
||||||
item.ident != "RuntimeFreezeReason" &&
|
item.ident != "RuntimeFreezeReason" &&
|
||||||
@@ -888,10 +887,11 @@ pub fn inject_runtime_type(_: TokenStream, tokens: TokenStream) -> TokenStream {
|
|||||||
{
|
{
|
||||||
return syn::Error::new_spanned(
|
return syn::Error::new_spanned(
|
||||||
item,
|
item,
|
||||||
"`#[inject_runtime_type]` can only be attached to `RuntimeCall`, `RuntimeEvent`, `RuntimeOrigin` or `PalletInfo`",
|
"`#[inject_runtime_type]` can only be attached to `RuntimeCall`, `RuntimeEvent`, \
|
||||||
|
`RuntimeTask`, `RuntimeOrigin` or `PalletInfo`",
|
||||||
)
|
)
|
||||||
.to_compile_error()
|
.to_compile_error()
|
||||||
.into();
|
.into()
|
||||||
}
|
}
|
||||||
tokens
|
tokens
|
||||||
}
|
}
|
||||||
@@ -1518,6 +1518,56 @@ pub fn composite_enum(_: TokenStream, _: TokenStream) -> TokenStream {
|
|||||||
pallet_macro_stub()
|
pallet_macro_stub()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// ---
|
||||||
|
///
|
||||||
|
/// **Rust-Analyzer users**: See the documentation of the Rust item in
|
||||||
|
/// `frame_support::pallet_macros::tasks_experimental`.
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn tasks_experimental(_: TokenStream, _: TokenStream) -> TokenStream {
|
||||||
|
pallet_macro_stub()
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// ---
|
||||||
|
///
|
||||||
|
/// **Rust-Analyzer users**: See the documentation of the Rust item in
|
||||||
|
/// `frame_support::pallet_macros::task_list`.
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn task_list(_: TokenStream, _: TokenStream) -> TokenStream {
|
||||||
|
pallet_macro_stub()
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// ---
|
||||||
|
///
|
||||||
|
/// **Rust-Analyzer users**: See the documentation of the Rust item in
|
||||||
|
/// `frame_support::pallet_macros::task_condition`.
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn task_condition(_: TokenStream, _: TokenStream) -> TokenStream {
|
||||||
|
pallet_macro_stub()
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// ---
|
||||||
|
///
|
||||||
|
/// **Rust-Analyzer users**: See the documentation of the Rust item in
|
||||||
|
/// `frame_support::pallet_macros::task_weight`.
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn task_weight(_: TokenStream, _: TokenStream) -> TokenStream {
|
||||||
|
pallet_macro_stub()
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// ---
|
||||||
|
///
|
||||||
|
/// **Rust-Analyzer users**: See the documentation of the Rust item in
|
||||||
|
/// `frame_support::pallet_macros::task_index`.
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn task_index(_: TokenStream, _: TokenStream) -> TokenStream {
|
||||||
|
pallet_macro_stub()
|
||||||
|
}
|
||||||
|
|
||||||
/// Can be attached to a module. Doing so will declare that module as importable into a pallet
|
/// Can be attached to a module. Doing so will declare that module as importable into a pallet
|
||||||
/// via [`#[import_section]`](`macro@import_section`).
|
/// via [`#[import_section]`](`macro@import_section`).
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ mod origin;
|
|||||||
mod pallet_struct;
|
mod pallet_struct;
|
||||||
mod storage;
|
mod storage;
|
||||||
mod store_trait;
|
mod store_trait;
|
||||||
|
mod tasks;
|
||||||
mod tt_default_parts;
|
mod tt_default_parts;
|
||||||
mod type_value;
|
mod type_value;
|
||||||
mod validate_unsigned;
|
mod validate_unsigned;
|
||||||
@@ -60,6 +61,7 @@ pub fn expand(mut def: Def) -> proc_macro2::TokenStream {
|
|||||||
let pallet_struct = pallet_struct::expand_pallet_struct(&mut def);
|
let pallet_struct = pallet_struct::expand_pallet_struct(&mut def);
|
||||||
let config = config::expand_config(&mut def);
|
let config = config::expand_config(&mut def);
|
||||||
let call = call::expand_call(&mut def);
|
let call = call::expand_call(&mut def);
|
||||||
|
let tasks = tasks::expand_tasks(&mut def);
|
||||||
let error = error::expand_error(&mut def);
|
let error = error::expand_error(&mut def);
|
||||||
let event = event::expand_event(&mut def);
|
let event = event::expand_event(&mut def);
|
||||||
let storages = storage::expand_storages(&mut def);
|
let storages = storage::expand_storages(&mut def);
|
||||||
@@ -100,6 +102,7 @@ storage item. Otherwise, all storage items are listed among [*Type Definitions*]
|
|||||||
#pallet_struct
|
#pallet_struct
|
||||||
#config
|
#config
|
||||||
#call
|
#call
|
||||||
|
#tasks
|
||||||
#error
|
#error
|
||||||
#event
|
#event
|
||||||
#storages
|
#storages
|
||||||
|
|||||||
@@ -0,0 +1,267 @@
|
|||||||
|
//! Contains logic for expanding task-related items.
|
||||||
|
|
||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// 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 derive_syn_parse::Parse;
|
||||||
|
use inflector::Inflector;
|
||||||
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
|
use quote::{format_ident, quote, ToTokens};
|
||||||
|
use syn::{parse_quote, spanned::Spanned, ItemEnum, ItemImpl};
|
||||||
|
|
||||||
|
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,
|
||||||
|
type_decl_bounded_generics: TokenStream2,
|
||||||
|
type_use_generics: TokenStream2,
|
||||||
|
) -> Self {
|
||||||
|
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()
|
||||||
|
};
|
||||||
|
let mut task_enum_def: TaskEnumDef = parse_quote! {
|
||||||
|
/// 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,
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
};
|
||||||
|
task_enum_def.type_use_generics = type_use_generics;
|
||||||
|
task_enum_def
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for TaskEnumDef {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||||
|
let item_enum = &self.item_enum;
|
||||||
|
let ident = &item_enum.ident;
|
||||||
|
let vis = &item_enum.vis;
|
||||||
|
let attrs = &item_enum.attrs;
|
||||||
|
let generics = &item_enum.generics;
|
||||||
|
let variants = &item_enum.variants;
|
||||||
|
let scrate = &self.scrate;
|
||||||
|
let type_use_generics = &self.type_use_generics;
|
||||||
|
if self.attr.is_some() {
|
||||||
|
// `item_enum` is short-hand / generated enum
|
||||||
|
tokens.extend(quote! {
|
||||||
|
#(#attrs)*
|
||||||
|
#[derive(
|
||||||
|
#scrate::CloneNoBound,
|
||||||
|
#scrate::EqNoBound,
|
||||||
|
#scrate::PartialEqNoBound,
|
||||||
|
#scrate::pallet_prelude::Encode,
|
||||||
|
#scrate::pallet_prelude::Decode,
|
||||||
|
#scrate::pallet_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<T>, #scrate::Never),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Config> 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)
|
||||||
|
tokens.extend(item_enum.to_token_stream());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents an already-expanded [`TasksDef`].
|
||||||
|
#[derive(Parse)]
|
||||||
|
pub struct ExpandedTasksDef {
|
||||||
|
pub task_item_impl: ItemImpl,
|
||||||
|
pub task_trait_impl: ItemImpl,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for TasksDef {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||||
|
let scrate = &self.scrate;
|
||||||
|
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 sp_std = quote!(#scrate::__private::sp_std);
|
||||||
|
let impl_generics = &self.item_impl.generics;
|
||||||
|
tokens.extend(quote! {
|
||||||
|
impl #impl_generics #enum_use
|
||||||
|
{
|
||||||
|
#(#task_fn_impls)*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl #impl_generics #scrate::traits::Task for #enum_use
|
||||||
|
{
|
||||||
|
type Enumeration = #sp_std::vec::IntoIter<#enum_use>;
|
||||||
|
|
||||||
|
fn iter() -> Self::Enumeration {
|
||||||
|
let mut all_tasks = #sp_std::vec![];
|
||||||
|
#(all_tasks
|
||||||
|
.extend(#task_iters.map(|(#(#task_arg_names),*)| #enum_ident::#task_fn_idents { #(#task_arg_names: #task_arg_names.clone()),* })
|
||||||
|
.collect::<#sp_std::vec::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<(), #scrate::pallet_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) -> #scrate::pallet_prelude::Weight {
|
||||||
|
match self.clone() {
|
||||||
|
#(#enum_ident::#task_fn_idents { #(#task_arg_names),* } => #task_weights,)*
|
||||||
|
Task::__Ignore(_, _) => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Expands the [`TasksDef`] in the enclosing [`Def`], if present, and returns its tokens.
|
||||||
|
///
|
||||||
|
/// This modifies the underlying [`Def`] in addition to returning any tokens that were added.
|
||||||
|
pub fn expand_tasks_impl(def: &mut Def) -> TokenStream2 {
|
||||||
|
let Some(tasks) = &mut def.tasks else { return quote!() };
|
||||||
|
let ExpandedTasksDef { task_item_impl, task_trait_impl } = parse_quote!(#tasks);
|
||||||
|
quote! {
|
||||||
|
#task_item_impl
|
||||||
|
#task_trait_impl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a fully-expanded [`TaskEnumDef`].
|
||||||
|
#[derive(Parse)]
|
||||||
|
pub struct ExpandedTaskEnum {
|
||||||
|
pub item_enum: ItemEnum,
|
||||||
|
pub debug_impl: ItemImpl,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Modifies a [`Def`] to expand the underlying [`TaskEnumDef`] if present, and also returns
|
||||||
|
/// its tokens. A blank [`TokenStream2`] is returned if no [`TaskEnumDef`] has been generated
|
||||||
|
/// or defined.
|
||||||
|
pub fn expand_task_enum(def: &mut Def) -> TokenStream2 {
|
||||||
|
let Some(task_enum) = &mut def.task_enum else { return quote!() };
|
||||||
|
let ExpandedTaskEnum { item_enum, debug_impl } = parse_quote!(#task_enum);
|
||||||
|
quote! {
|
||||||
|
#item_enum
|
||||||
|
#debug_impl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Modifies a [`Def`] to expand the underlying [`TasksDef`] and also generate a
|
||||||
|
/// [`TaskEnumDef`] if applicable. The tokens for these items are returned if they are created.
|
||||||
|
pub fn expand_tasks(def: &mut Def) -> TokenStream2 {
|
||||||
|
if let Some(tasks_def) = &def.tasks {
|
||||||
|
if def.task_enum.is_none() {
|
||||||
|
def.task_enum = Some(TaskEnumDef::generate(
|
||||||
|
&tasks_def,
|
||||||
|
def.type_decl_bounded_generics(tasks_def.item_impl.span()),
|
||||||
|
def.type_use_generics(tasks_def.item_impl.span()),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let tasks_extra_output = expand_tasks_impl(def);
|
||||||
|
let task_enum_extra_output = expand_task_enum(def);
|
||||||
|
quote! {
|
||||||
|
#tasks_extra_output
|
||||||
|
#task_enum_extra_output
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,6 +31,8 @@ pub fn expand_tt_default_parts(def: &mut Def) -> proc_macro2::TokenStream {
|
|||||||
|
|
||||||
let call_part = def.call.as_ref().map(|_| quote::quote!(Call,));
|
let call_part = def.call.as_ref().map(|_| quote::quote!(Call,));
|
||||||
|
|
||||||
|
let task_part = def.task_enum.as_ref().map(|_| quote::quote!(Task,));
|
||||||
|
|
||||||
let storage_part = (!def.storages.is_empty()).then(|| quote::quote!(Storage,));
|
let storage_part = (!def.storages.is_empty()).then(|| quote::quote!(Storage,));
|
||||||
|
|
||||||
let event_part = def.event.as_ref().map(|event| {
|
let event_part = def.event.as_ref().map(|event| {
|
||||||
@@ -99,7 +101,7 @@ pub fn expand_tt_default_parts(def: &mut Def) -> proc_macro2::TokenStream {
|
|||||||
tokens = [{
|
tokens = [{
|
||||||
expanded::{
|
expanded::{
|
||||||
Pallet, #call_part #storage_part #event_part #error_part #origin_part #config_part
|
Pallet, #call_part #storage_part #event_part #error_part #origin_part #config_part
|
||||||
#inherent_part #validate_unsigned_part #freeze_reason_part
|
#inherent_part #validate_unsigned_part #freeze_reason_part #task_part
|
||||||
#hold_reason_part #lock_id_part #slash_reason_part
|
#hold_reason_part #lock_id_part #slash_reason_part
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
|
|||||||
@@ -286,8 +286,7 @@ impl CallDef {
|
|||||||
if weight_attrs.is_empty() && dev_mode {
|
if weight_attrs.is_empty() && dev_mode {
|
||||||
// inject a default O(1) weight when dev mode is enabled and no weight has
|
// inject a default O(1) weight when dev mode is enabled and no weight has
|
||||||
// been specified on the call
|
// been specified on the call
|
||||||
let empty_weight: syn::Expr = syn::parse(quote::quote!(0).into())
|
let empty_weight: syn::Expr = syn::parse_quote!(0);
|
||||||
.expect("we are parsing a quoted string; qed");
|
|
||||||
weight_attrs.push(FunctionAttr::Weight(empty_weight));
|
weight_attrs.push(FunctionAttr::Weight(empty_weight));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,11 +26,14 @@ pub mod keyword {
|
|||||||
syn::custom_keyword!(HoldReason);
|
syn::custom_keyword!(HoldReason);
|
||||||
syn::custom_keyword!(LockId);
|
syn::custom_keyword!(LockId);
|
||||||
syn::custom_keyword!(SlashReason);
|
syn::custom_keyword!(SlashReason);
|
||||||
|
syn::custom_keyword!(Task);
|
||||||
|
|
||||||
pub enum CompositeKeyword {
|
pub enum CompositeKeyword {
|
||||||
FreezeReason(FreezeReason),
|
FreezeReason(FreezeReason),
|
||||||
HoldReason(HoldReason),
|
HoldReason(HoldReason),
|
||||||
LockId(LockId),
|
LockId(LockId),
|
||||||
SlashReason(SlashReason),
|
SlashReason(SlashReason),
|
||||||
|
Task(Task),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToTokens for CompositeKeyword {
|
impl ToTokens for CompositeKeyword {
|
||||||
@@ -41,6 +44,7 @@ pub mod keyword {
|
|||||||
HoldReason(inner) => inner.to_tokens(tokens),
|
HoldReason(inner) => inner.to_tokens(tokens),
|
||||||
LockId(inner) => inner.to_tokens(tokens),
|
LockId(inner) => inner.to_tokens(tokens),
|
||||||
SlashReason(inner) => inner.to_tokens(tokens),
|
SlashReason(inner) => inner.to_tokens(tokens),
|
||||||
|
Task(inner) => inner.to_tokens(tokens),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,6 +60,8 @@ pub mod keyword {
|
|||||||
Ok(Self::LockId(input.parse()?))
|
Ok(Self::LockId(input.parse()?))
|
||||||
} else if lookahead.peek(SlashReason) {
|
} else if lookahead.peek(SlashReason) {
|
||||||
Ok(Self::SlashReason(input.parse()?))
|
Ok(Self::SlashReason(input.parse()?))
|
||||||
|
} else if lookahead.peek(Task) {
|
||||||
|
Ok(Self::Task(input.parse()?))
|
||||||
} else {
|
} else {
|
||||||
Err(lookahead.error())
|
Err(lookahead.error())
|
||||||
}
|
}
|
||||||
@@ -71,6 +77,7 @@ pub mod keyword {
|
|||||||
match self {
|
match self {
|
||||||
FreezeReason(_) => "FreezeReason",
|
FreezeReason(_) => "FreezeReason",
|
||||||
HoldReason(_) => "HoldReason",
|
HoldReason(_) => "HoldReason",
|
||||||
|
Task(_) => "Task",
|
||||||
LockId(_) => "LockId",
|
LockId(_) => "LockId",
|
||||||
SlashReason(_) => "SlashReason",
|
SlashReason(_) => "SlashReason",
|
||||||
}
|
}
|
||||||
@@ -80,7 +87,7 @@ pub mod keyword {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct CompositeDef {
|
pub struct CompositeDef {
|
||||||
/// The index of the HoldReason item in the pallet module.
|
/// The index of the CompositeDef item in the pallet module.
|
||||||
pub index: usize,
|
pub index: usize,
|
||||||
/// The composite keyword used (contains span).
|
/// The composite keyword used (contains span).
|
||||||
pub composite_keyword: keyword::CompositeKeyword,
|
pub composite_keyword: keyword::CompositeKeyword,
|
||||||
|
|||||||
@@ -33,11 +33,16 @@ pub mod inherent;
|
|||||||
pub mod origin;
|
pub mod origin;
|
||||||
pub mod pallet_struct;
|
pub mod pallet_struct;
|
||||||
pub mod storage;
|
pub mod storage;
|
||||||
|
pub mod tasks;
|
||||||
pub mod type_value;
|
pub mod type_value;
|
||||||
pub mod validate_unsigned;
|
pub mod validate_unsigned;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod tests;
|
||||||
|
|
||||||
use composite::{keyword::CompositeKeyword, CompositeDef};
|
use composite::{keyword::CompositeKeyword, CompositeDef};
|
||||||
use frame_support_procedural_tools::generate_access_from_frame_or_crate;
|
use frame_support_procedural_tools::generate_access_from_frame_or_crate;
|
||||||
|
use quote::ToTokens;
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
|
|
||||||
/// Parsed definition of a pallet.
|
/// Parsed definition of a pallet.
|
||||||
@@ -49,6 +54,8 @@ pub struct Def {
|
|||||||
pub pallet_struct: pallet_struct::PalletStructDef,
|
pub pallet_struct: pallet_struct::PalletStructDef,
|
||||||
pub hooks: Option<hooks::HooksDef>,
|
pub hooks: Option<hooks::HooksDef>,
|
||||||
pub call: Option<call::CallDef>,
|
pub call: Option<call::CallDef>,
|
||||||
|
pub tasks: Option<tasks::TasksDef>,
|
||||||
|
pub task_enum: Option<tasks::TaskEnumDef>,
|
||||||
pub storages: Vec<storage::StorageDef>,
|
pub storages: Vec<storage::StorageDef>,
|
||||||
pub error: Option<error::ErrorDef>,
|
pub error: Option<error::ErrorDef>,
|
||||||
pub event: Option<event::EventDef>,
|
pub event: Option<event::EventDef>,
|
||||||
@@ -84,6 +91,8 @@ impl Def {
|
|||||||
let mut pallet_struct = None;
|
let mut pallet_struct = None;
|
||||||
let mut hooks = None;
|
let mut hooks = None;
|
||||||
let mut call = None;
|
let mut call = None;
|
||||||
|
let mut tasks = None;
|
||||||
|
let mut task_enum = None;
|
||||||
let mut error = None;
|
let mut error = None;
|
||||||
let mut event = None;
|
let mut event = None;
|
||||||
let mut origin = None;
|
let mut origin = None;
|
||||||
@@ -118,6 +127,32 @@ impl Def {
|
|||||||
},
|
},
|
||||||
Some(PalletAttr::RuntimeCall(cw, span)) if call.is_none() =>
|
Some(PalletAttr::RuntimeCall(cw, span)) if call.is_none() =>
|
||||||
call = Some(call::CallDef::try_from(span, index, item, dev_mode, cw)?),
|
call = Some(call::CallDef::try_from(span, index, item, dev_mode, cw)?),
|
||||||
|
Some(PalletAttr::Tasks(_)) 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! {
|
||||||
|
#[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() =>
|
Some(PalletAttr::Error(span)) if error.is_none() =>
|
||||||
error = Some(error::ErrorDef::try_from(span, index, item)?),
|
error = Some(error::ErrorDef::try_from(span, index, item)?),
|
||||||
Some(PalletAttr::RuntimeEvent(span)) if event.is_none() =>
|
Some(PalletAttr::RuntimeEvent(span)) if event.is_none() =>
|
||||||
@@ -190,6 +225,8 @@ impl Def {
|
|||||||
return Err(syn::Error::new(item_span, msg))
|
return Err(syn::Error::new(item_span, msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Self::resolve_tasks(&item_span, &mut tasks, &mut task_enum, items)?;
|
||||||
|
|
||||||
let def = Def {
|
let def = Def {
|
||||||
item,
|
item,
|
||||||
config: config
|
config: config
|
||||||
@@ -198,6 +235,8 @@ impl Def {
|
|||||||
.ok_or_else(|| syn::Error::new(item_span, "Missing `#[pallet::pallet]`"))?,
|
.ok_or_else(|| syn::Error::new(item_span, "Missing `#[pallet::pallet]`"))?,
|
||||||
hooks,
|
hooks,
|
||||||
call,
|
call,
|
||||||
|
tasks,
|
||||||
|
task_enum,
|
||||||
extra_constants,
|
extra_constants,
|
||||||
genesis_config,
|
genesis_config,
|
||||||
genesis_build,
|
genesis_build,
|
||||||
@@ -220,6 +259,99 @@ impl Def {
|
|||||||
Ok(def)
|
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 impling 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 `Event` is consistent with the definition, i.e. it is declared
|
/// Check that usage of trait `Event` is consistent with the definition, i.e. it is declared
|
||||||
/// and trait defines type RuntimeEvent, or not declared and no trait associated type.
|
/// and trait defines type RuntimeEvent, or not declared and no trait associated type.
|
||||||
fn check_event_usage(&self) -> syn::Result<()> {
|
fn check_event_usage(&self) -> syn::Result<()> {
|
||||||
@@ -408,6 +540,11 @@ impl GenericKind {
|
|||||||
mod keyword {
|
mod keyword {
|
||||||
syn::custom_keyword!(origin);
|
syn::custom_keyword!(origin);
|
||||||
syn::custom_keyword!(call);
|
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!(weight);
|
||||||
syn::custom_keyword!(event);
|
syn::custom_keyword!(event);
|
||||||
syn::custom_keyword!(config);
|
syn::custom_keyword!(config);
|
||||||
@@ -472,6 +609,11 @@ enum PalletAttr {
|
|||||||
/// instead of the zero weight. So to say: it works together with `dev_mode`.
|
/// instead of the zero weight. So to say: it works together with `dev_mode`.
|
||||||
RuntimeCall(Option<InheritedCallWeightAttr>, proc_macro2::Span),
|
RuntimeCall(Option<InheritedCallWeightAttr>, proc_macro2::Span),
|
||||||
Error(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),
|
RuntimeEvent(proc_macro2::Span),
|
||||||
RuntimeOrigin(proc_macro2::Span),
|
RuntimeOrigin(proc_macro2::Span),
|
||||||
Inherent(proc_macro2::Span),
|
Inherent(proc_macro2::Span),
|
||||||
@@ -490,8 +632,13 @@ impl PalletAttr {
|
|||||||
Self::Config(span, _) => *span,
|
Self::Config(span, _) => *span,
|
||||||
Self::Pallet(span) => *span,
|
Self::Pallet(span) => *span,
|
||||||
Self::Hooks(span) => *span,
|
Self::Hooks(span) => *span,
|
||||||
Self::RuntimeCall(_, span) => *span,
|
Self::Tasks(span) => *span,
|
||||||
|
Self::TaskCondition(span) => *span,
|
||||||
|
Self::TaskIndex(span) => *span,
|
||||||
|
Self::TaskList(span) => *span,
|
||||||
Self::Error(span) => *span,
|
Self::Error(span) => *span,
|
||||||
|
Self::RuntimeTask(span) => *span,
|
||||||
|
Self::RuntimeCall(_, span) => *span,
|
||||||
Self::RuntimeEvent(span) => *span,
|
Self::RuntimeEvent(span) => *span,
|
||||||
Self::RuntimeOrigin(span) => *span,
|
Self::RuntimeOrigin(span) => *span,
|
||||||
Self::Inherent(span) => *span,
|
Self::Inherent(span) => *span,
|
||||||
@@ -535,6 +682,16 @@ impl syn::parse::Parse for PalletAttr {
|
|||||||
false => Some(InheritedCallWeightAttr::parse(&content)?),
|
false => Some(InheritedCallWeightAttr::parse(&content)?),
|
||||||
};
|
};
|
||||||
Ok(PalletAttr::RuntimeCall(attr, span))
|
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) {
|
} else if lookahead.peek(keyword::error) {
|
||||||
Ok(PalletAttr::Error(content.parse::<keyword::error>()?.span()))
|
Ok(PalletAttr::Error(content.parse::<keyword::error>()?.span()))
|
||||||
} else if lookahead.peek(keyword::event) {
|
} else if lookahead.peek(keyword::event) {
|
||||||
|
|||||||
@@ -0,0 +1,968 @@
|
|||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// 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 derive_syn_parse::Parse;
|
||||||
|
use frame_support_procedural_tools::generate_access_from_frame_or_crate;
|
||||||
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
|
use quote::{quote, ToTokens};
|
||||||
|
use syn::{
|
||||||
|
parse::ParseStream,
|
||||||
|
parse2,
|
||||||
|
spanned::Spanned,
|
||||||
|
token::{Bracket, Paren, PathSep, Pound},
|
||||||
|
Attribute, Error, Expr, Ident, ImplItem, ImplItemFn, ItemEnum, ItemImpl, LitInt, Path,
|
||||||
|
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,
|
||||||
|
/// Path to `frame_support`
|
||||||
|
pub scrate: Path,
|
||||||
|
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();
|
||||||
|
|
||||||
|
// We do this here because it would be improper to do something fallible like this at
|
||||||
|
// the expansion phase. Fallible stuff should happen during parsing.
|
||||||
|
let scrate = generate_access_from_frame_or_crate("frame-support")?;
|
||||||
|
|
||||||
|
Ok(TasksDef { tasks_attr, item_impl, tasks, scrate, 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, Debug)]
|
||||||
|
pub struct TaskEnumDef {
|
||||||
|
pub attr: Option<PalletTaskEnumAttr>,
|
||||||
|
pub item_enum: ItemEnum,
|
||||||
|
pub scrate: Path,
|
||||||
|
pub type_use_generics: TokenStream2,
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
// We do this here because it would be improper to do something fallible like this at
|
||||||
|
// the expansion phase. Fallible stuff should happen during parsing.
|
||||||
|
let scrate = generate_access_from_frame_or_crate("frame-support")?;
|
||||||
|
|
||||||
|
let type_use_generics = quote!(T);
|
||||||
|
|
||||||
|
Ok(TaskEnumDef { attr, item_enum, scrate, type_use_generics })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 normal_attrs: Vec<Attribute>,
|
||||||
|
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, normal_attrs) = partition_task_attrs(&item);
|
||||||
|
|
||||||
|
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,
|
||||||
|
normal_attrs,
|
||||||
|
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 {
|
||||||
|
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 {
|
||||||
|
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;
|
||||||
|
}),
|
||||||
|
"expected `enum`"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,264 @@
|
|||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// 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")]
|
||||||
|
/// #[frame_support::pallet]
|
||||||
|
/// pub mod pallet {
|
||||||
|
/// #[pallet::config]
|
||||||
|
/// pub trait Config: frame_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\\]`")]
|
||||||
|
/// #[frame_support::pallet]
|
||||||
|
/// pub mod pallet {
|
||||||
|
/// #[pallet::config]
|
||||||
|
/// pub trait Config: frame_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 pallet-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")]
|
||||||
|
#[frame_support::pallet]
|
||||||
|
pub mod pallet {
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_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\\]`")]
|
||||||
|
#[frame_support::pallet]
|
||||||
|
pub mod pallet {
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_system::Config {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_pallet_missing_config() {
|
||||||
|
assert_pallet_parse_error! {
|
||||||
|
#[manifest_dir("../../examples/basic")]
|
||||||
|
#[error_regex("Missing `\\#\\[pallet::config\\]`")]
|
||||||
|
#[frame_support::pallet]
|
||||||
|
pub mod pallet {
|
||||||
|
#[pallet::pallet]
|
||||||
|
pub struct Pallet<T>(_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,240 @@
|
|||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// 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")]
|
||||||
|
#[frame_support::pallet]
|
||||||
|
pub mod pallet {
|
||||||
|
#[pallet::task_enum]
|
||||||
|
pub enum Task<T: Config> {
|
||||||
|
Something,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_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")]
|
||||||
|
#[frame_support::pallet]
|
||||||
|
pub mod pallet {
|
||||||
|
#[pallet::wrong_attribute]
|
||||||
|
pub enum Task<T: Config> {
|
||||||
|
Something,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pallet::task_list]
|
||||||
|
impl<T: Config> frame_support::traits::Task for Task<T>
|
||||||
|
where
|
||||||
|
T: TypeInfo,
|
||||||
|
{}
|
||||||
|
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_system::Config {}
|
||||||
|
|
||||||
|
#[pallet::pallet]
|
||||||
|
pub struct Pallet<T>(_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_pallet_missing_task_enum() {
|
||||||
|
assert_pallet_parses! {
|
||||||
|
#[manifest_dir("../../examples/basic")]
|
||||||
|
#[frame_support::pallet]
|
||||||
|
pub mod pallet {
|
||||||
|
#[pallet::tasks_experimental]
|
||||||
|
#[cfg(test)] // aha, this means it's being eaten
|
||||||
|
impl<T: Config> frame_support::traits::Task for Task<T>
|
||||||
|
where
|
||||||
|
T: TypeInfo,
|
||||||
|
{}
|
||||||
|
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_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.")]
|
||||||
|
#[frame_support::pallet]
|
||||||
|
pub mod pallet {
|
||||||
|
pub enum MyCustomTaskEnum<T: Config> {
|
||||||
|
Something,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pallet::task_list]
|
||||||
|
pub fn something() {
|
||||||
|
println!("hey");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_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.*")]
|
||||||
|
#[frame_support::pallet]
|
||||||
|
pub mod pallet {
|
||||||
|
|
||||||
|
impl<T: Config> frame_support::traits::Task for Task<T>
|
||||||
|
where
|
||||||
|
T: TypeInfo,
|
||||||
|
{
|
||||||
|
type Enumeration = sp_std::vec::IntoIter<Task<T>>;
|
||||||
|
|
||||||
|
fn iter() -> Self::Enumeration {
|
||||||
|
sp_std::vec![Task::increment, Task::decrement].into_iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_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")]
|
||||||
|
#[frame_support::pallet]
|
||||||
|
pub mod pallet {
|
||||||
|
pub enum MyCustomTaskEnum<T: Config> {
|
||||||
|
Something,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pallet::tasks_experimental]
|
||||||
|
impl<T: Config> frame_support::traits::Task for MyCustomTaskEnum<T>
|
||||||
|
where
|
||||||
|
T: TypeInfo,
|
||||||
|
{}
|
||||||
|
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_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")]
|
||||||
|
#[frame_support::pallet]
|
||||||
|
pub mod pallet {
|
||||||
|
#[pallet::task_enum]
|
||||||
|
pub enum MyCustomTaskEnum<T: Config> {
|
||||||
|
Something,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Config> frame_support::traits::Task for MyCustomTaskEnum<T>
|
||||||
|
where
|
||||||
|
T: TypeInfo,
|
||||||
|
{}
|
||||||
|
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_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")]
|
||||||
|
#[frame_support::pallet]
|
||||||
|
pub mod pallet {
|
||||||
|
pub enum MyCustomTaskEnum<T: Config> {
|
||||||
|
Something,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Config> frame_support::traits::Task for MyCustomTaskEnum<T>
|
||||||
|
where
|
||||||
|
T: TypeInfo,
|
||||||
|
{}
|
||||||
|
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_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")]
|
||||||
|
#[frame_support::pallet]
|
||||||
|
pub mod pallet {
|
||||||
|
pub enum WrongIdent<T: Config> {
|
||||||
|
Something,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pallet::tasks_experimental]
|
||||||
|
impl<T: Config> frame_support::traits::Task for MyCustomTaskEnum<T>
|
||||||
|
where
|
||||||
|
T: TypeInfo,
|
||||||
|
{}
|
||||||
|
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_system::Config {}
|
||||||
|
|
||||||
|
#[pallet::pallet]
|
||||||
|
pub struct Pallet<T>(_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -695,6 +695,7 @@ mod weight_tests {
|
|||||||
type BaseCallFilter: crate::traits::Contains<Self::RuntimeCall>;
|
type BaseCallFilter: crate::traits::Contains<Self::RuntimeCall>;
|
||||||
type RuntimeOrigin;
|
type RuntimeOrigin;
|
||||||
type RuntimeCall;
|
type RuntimeCall;
|
||||||
|
type RuntimeTask;
|
||||||
type PalletInfo: crate::traits::PalletInfo;
|
type PalletInfo: crate::traits::PalletInfo;
|
||||||
type DbWeight: Get<crate::weights::RuntimeDbWeight>;
|
type DbWeight: Get<crate::weights::RuntimeDbWeight>;
|
||||||
}
|
}
|
||||||
@@ -791,6 +792,7 @@ mod weight_tests {
|
|||||||
type BaseCallFilter = crate::traits::Everything;
|
type BaseCallFilter = crate::traits::Everything;
|
||||||
type RuntimeOrigin = RuntimeOrigin;
|
type RuntimeOrigin = RuntimeOrigin;
|
||||||
type RuntimeCall = RuntimeCall;
|
type RuntimeCall = RuntimeCall;
|
||||||
|
type RuntimeTask = RuntimeTask;
|
||||||
type DbWeight = DbWeight;
|
type DbWeight = DbWeight;
|
||||||
type PalletInfo = PalletInfo;
|
type PalletInfo = PalletInfo;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -849,7 +849,7 @@ pub mod pallet_prelude {
|
|||||||
},
|
},
|
||||||
traits::{
|
traits::{
|
||||||
BuildGenesisConfig, ConstU32, EnsureOrigin, Get, GetDefault, GetStorageVersion, Hooks,
|
BuildGenesisConfig, ConstU32, EnsureOrigin, Get, GetDefault, GetStorageVersion, Hooks,
|
||||||
IsType, PalletInfoAccess, StorageInfoTrait, StorageVersion, TypedGet,
|
IsType, PalletInfoAccess, StorageInfoTrait, StorageVersion, Task, TypedGet,
|
||||||
},
|
},
|
||||||
Blake2_128, Blake2_128Concat, Blake2_256, CloneNoBound, DebugNoBound, EqNoBound, Identity,
|
Blake2_128, Blake2_128Concat, Blake2_256, CloneNoBound, DebugNoBound, EqNoBound, Identity,
|
||||||
PartialEqNoBound, RuntimeDebugNoBound, Twox128, Twox256, Twox64Concat,
|
PartialEqNoBound, RuntimeDebugNoBound, Twox128, Twox256, Twox64Concat,
|
||||||
@@ -2674,6 +2674,61 @@ pub mod pallet_macros {
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub use frame_support_procedural::storage;
|
pub use frame_support_procedural::storage;
|
||||||
|
/// This attribute is attached to a function inside an `impl` block annoated with
|
||||||
|
/// [`pallet::tasks_experimental`](`tasks_experimental`) to define the conditions for a
|
||||||
|
/// given work item to be valid.
|
||||||
|
///
|
||||||
|
/// It takes a closure as input, which is then used to define the condition. The closure
|
||||||
|
/// should have the same signature as the function it is attached to, except that it should
|
||||||
|
/// return a `bool` instead.
|
||||||
|
pub use frame_support_procedural::task_condition;
|
||||||
|
/// This attribute is attached to a function inside an `impl` block annoated with
|
||||||
|
/// [`pallet::tasks_experimental`](`tasks_experimental`) to define the index of a given
|
||||||
|
/// work item.
|
||||||
|
///
|
||||||
|
/// It takes an integer literal as input, which is then used to define the index. This
|
||||||
|
/// index should be unique for each function in the `impl` block.
|
||||||
|
pub use frame_support_procedural::task_index;
|
||||||
|
/// This attribute is attached to a function inside an `impl` block annoated with
|
||||||
|
/// [`pallet::tasks_experimental`](`tasks_experimental`) to define an iterator over the
|
||||||
|
/// available work items for a task.
|
||||||
|
///
|
||||||
|
/// It takes an iterator as input that yields a tuple with same types as the function
|
||||||
|
/// arguments.
|
||||||
|
pub use frame_support_procedural::task_list;
|
||||||
|
/// This attribute is attached to a function inside an `impl` block annoated with
|
||||||
|
/// [`pallet::tasks_experimental`](`tasks_experimental`) define the weight of a given work
|
||||||
|
/// item.
|
||||||
|
///
|
||||||
|
/// It takes a closure as input, which should return a `Weight` value.
|
||||||
|
pub use frame_support_procedural::task_weight;
|
||||||
|
/// Allows you to define some service work that can be recognized by a script or an
|
||||||
|
/// off-chain worker. Such a script can then create and submit all such work items at any
|
||||||
|
/// given time.
|
||||||
|
///
|
||||||
|
/// These work items are defined as instances of the [`Task`](frame_support::traits::Task)
|
||||||
|
/// trait. [`pallet:tasks_experimental`](`tasks_experimental`) when attached to an `impl`
|
||||||
|
/// block inside a pallet, will generate an enum `Task<T>` whose variants are mapped to
|
||||||
|
/// functions inside this `impl` block.
|
||||||
|
///
|
||||||
|
/// Each such function must have the following set of attributes:
|
||||||
|
///
|
||||||
|
/// * [`pallet::task_list`](`task_list`)
|
||||||
|
/// * [`pallet::task_condition`](`task_condition`)
|
||||||
|
/// * [`pallet::task_weight`](`task_weight`)
|
||||||
|
/// * [`pallet::task_index`](`task_index`)
|
||||||
|
///
|
||||||
|
/// All of such Tasks are then aggregated into a `RuntimeTask` by
|
||||||
|
/// [`construct_runtime`](frame_support::construct_runtime).
|
||||||
|
///
|
||||||
|
/// Finally, the `RuntimeTask` can then used by a script or off-chain worker to create and
|
||||||
|
/// submit such tasks via an extrinsic defined in `frame_system` called `do_task`.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
#[doc = docify::embed!("src/tests/tasks.rs", tasks_example)]
|
||||||
|
/// Now, this can be executed as follows:
|
||||||
|
#[doc = docify::embed!("src/tests/tasks.rs", tasks_work)]
|
||||||
|
pub use frame_support_procedural::tasks_experimental;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[deprecated(note = "Will be removed after July 2023; Use `sp_runtime::traits` directly instead.")]
|
#[deprecated(note = "Will be removed after July 2023; Use `sp_runtime::traits` directly instead.")]
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ mod tests {
|
|||||||
type BaseCallFilter: crate::traits::Contains<Self::RuntimeCall>;
|
type BaseCallFilter: crate::traits::Contains<Self::RuntimeCall>;
|
||||||
type RuntimeOrigin;
|
type RuntimeOrigin;
|
||||||
type RuntimeCall;
|
type RuntimeCall;
|
||||||
|
type RuntimeTask;
|
||||||
type PalletInfo: crate::traits::PalletInfo;
|
type PalletInfo: crate::traits::PalletInfo;
|
||||||
type DbWeight: Get<crate::weights::RuntimeDbWeight>;
|
type DbWeight: Get<crate::weights::RuntimeDbWeight>;
|
||||||
}
|
}
|
||||||
@@ -129,6 +130,7 @@ mod tests {
|
|||||||
type BaseCallFilter = crate::traits::Everything;
|
type BaseCallFilter = crate::traits::Everything;
|
||||||
type RuntimeOrigin = RuntimeOrigin;
|
type RuntimeOrigin = RuntimeOrigin;
|
||||||
type RuntimeCall = RuntimeCall;
|
type RuntimeCall = RuntimeCall;
|
||||||
|
type RuntimeTask = RuntimeTask;
|
||||||
type PalletInfo = PalletInfo;
|
type PalletInfo = PalletInfo;
|
||||||
type DbWeight = ();
|
type DbWeight = ();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use frame_support_procedural::import_section;
|
||||||
use sp_io::{MultiRemovalResults, TestExternalities};
|
use sp_io::{MultiRemovalResults, TestExternalities};
|
||||||
use sp_metadata_ir::{
|
use sp_metadata_ir::{
|
||||||
PalletStorageMetadataIR, StorageEntryMetadataIR, StorageEntryModifierIR, StorageEntryTypeIR,
|
PalletStorageMetadataIR, StorageEntryMetadataIR, StorageEntryModifierIR, StorageEntryTypeIR,
|
||||||
@@ -27,13 +28,15 @@ pub use self::frame_system::{pallet_prelude::*, Config, Pallet};
|
|||||||
|
|
||||||
mod inject_runtime_type;
|
mod inject_runtime_type;
|
||||||
mod storage_alias;
|
mod storage_alias;
|
||||||
|
mod tasks;
|
||||||
|
|
||||||
|
#[import_section(tasks::tasks_example)]
|
||||||
#[pallet]
|
#[pallet]
|
||||||
pub mod frame_system {
|
pub mod frame_system {
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
use super::{frame_system, frame_system::pallet_prelude::*};
|
use super::{frame_system, frame_system::pallet_prelude::*};
|
||||||
pub use crate::dispatch::RawOrigin;
|
pub use crate::dispatch::RawOrigin;
|
||||||
use crate::pallet_prelude::*;
|
use crate::{pallet_prelude::*, traits::tasks::Task as TaskTrait};
|
||||||
|
|
||||||
pub mod config_preludes {
|
pub mod config_preludes {
|
||||||
use super::{inject_runtime_type, DefaultConfig};
|
use super::{inject_runtime_type, DefaultConfig};
|
||||||
@@ -49,6 +52,8 @@ pub mod frame_system {
|
|||||||
type RuntimeCall = ();
|
type RuntimeCall = ();
|
||||||
#[inject_runtime_type]
|
#[inject_runtime_type]
|
||||||
type PalletInfo = ();
|
type PalletInfo = ();
|
||||||
|
#[inject_runtime_type]
|
||||||
|
type RuntimeTask = ();
|
||||||
type DbWeight = ();
|
type DbWeight = ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,6 +74,8 @@ pub mod frame_system {
|
|||||||
#[pallet::no_default_bounds]
|
#[pallet::no_default_bounds]
|
||||||
type RuntimeCall;
|
type RuntimeCall;
|
||||||
#[pallet::no_default_bounds]
|
#[pallet::no_default_bounds]
|
||||||
|
type RuntimeTask: crate::traits::tasks::Task;
|
||||||
|
#[pallet::no_default_bounds]
|
||||||
type PalletInfo: crate::traits::PalletInfo;
|
type PalletInfo: crate::traits::PalletInfo;
|
||||||
type DbWeight: Get<crate::weights::RuntimeDbWeight>;
|
type DbWeight: Get<crate::weights::RuntimeDbWeight>;
|
||||||
}
|
}
|
||||||
@@ -77,13 +84,33 @@ pub mod frame_system {
|
|||||||
pub enum Error<T> {
|
pub enum Error<T> {
|
||||||
/// Required by construct_runtime
|
/// Required by construct_runtime
|
||||||
CallFiltered,
|
CallFiltered,
|
||||||
|
/// Used in tasks example.
|
||||||
|
NotFound,
|
||||||
|
/// The specified [`Task`] is not valid.
|
||||||
|
InvalidTask,
|
||||||
|
/// The specified [`Task`] failed during execution.
|
||||||
|
FailedTask,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pallet::origin]
|
#[pallet::origin]
|
||||||
pub type Origin<T> = RawOrigin<<T as Config>::AccountId>;
|
pub type Origin<T> = RawOrigin<<T as Config>::AccountId>;
|
||||||
|
|
||||||
#[pallet::call]
|
#[pallet::call]
|
||||||
impl<T: Config> Pallet<T> {}
|
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]
|
#[pallet::storage]
|
||||||
pub type Data<T> = StorageMap<_, Twox64Concat, u32, u64, ValueQuery>;
|
pub type Data<T> = StorageMap<_, Twox64Concat, u32, u64, ValueQuery>;
|
||||||
@@ -169,6 +196,14 @@ pub mod frame_system {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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 pallet_prelude {
|
pub mod pallet_prelude {
|
||||||
pub type OriginFor<T> = <T as super::Config>::RuntimeOrigin;
|
pub type OriginFor<T> = <T as super::Config>::RuntimeOrigin;
|
||||||
|
|
||||||
@@ -622,6 +657,24 @@ fn expected_metadata() -> PalletStorageMetadataIR {
|
|||||||
default: vec![0],
|
default: vec![0],
|
||||||
docs: vec![],
|
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."],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// 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::{
|
||||||
|
assert_ok,
|
||||||
|
tests::{
|
||||||
|
frame_system::{Numbers, Total},
|
||||||
|
new_test_ext, Runtime, RuntimeOrigin, RuntimeTask, System,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use frame_support_procedural::pallet_section;
|
||||||
|
|
||||||
|
#[pallet_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() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
Numbers::<Runtime>::insert(0, 1);
|
||||||
|
|
||||||
|
let task = RuntimeTask::System(super::frame_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));
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -123,6 +123,9 @@ pub use safe_mode::{SafeMode, SafeModeError, SafeModeNotify};
|
|||||||
mod tx_pause;
|
mod tx_pause;
|
||||||
pub use tx_pause::{TransactionPause, TransactionPauseError};
|
pub use tx_pause::{TransactionPause, TransactionPauseError};
|
||||||
|
|
||||||
|
pub mod tasks;
|
||||||
|
pub use tasks::Task;
|
||||||
|
|
||||||
#[cfg(feature = "try-runtime")]
|
#[cfg(feature = "try-runtime")]
|
||||||
mod try_runtime;
|
mod try_runtime;
|
||||||
#[cfg(feature = "try-runtime")]
|
#[cfg(feature = "try-runtime")]
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//! Contains the [`Task`] trait, which defines a general-purpose way for defining and executing
|
||||||
|
//! service work, and supporting types.
|
||||||
|
|
||||||
|
use codec::FullCodec;
|
||||||
|
use scale_info::TypeInfo;
|
||||||
|
use sp_runtime::DispatchError;
|
||||||
|
use sp_std::{fmt::Debug, iter::Iterator, vec, vec::IntoIter};
|
||||||
|
use sp_weights::Weight;
|
||||||
|
|
||||||
|
/// Contain's re-exports of all the supporting types for the [`Task`] trait. Used in the macro
|
||||||
|
/// expansion of `RuntimeTask`.
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub mod __private {
|
||||||
|
pub use codec::FullCodec;
|
||||||
|
pub use scale_info::TypeInfo;
|
||||||
|
pub use sp_runtime::DispatchError;
|
||||||
|
pub use sp_std::{fmt::Debug, iter::Iterator, vec, vec::IntoIter};
|
||||||
|
pub use sp_weights::Weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A general-purpose trait which defines a type of service work (i.e., work to performed by an
|
||||||
|
/// off-chain worker) including methods for enumerating, validating, indexing, and running
|
||||||
|
/// tasks of this type.
|
||||||
|
pub trait Task: Sized + FullCodec + TypeInfo + Clone + Debug + PartialEq + Eq {
|
||||||
|
/// An [`Iterator`] over tasks of this type used as the return type for `enumerate`.
|
||||||
|
type Enumeration: Iterator;
|
||||||
|
|
||||||
|
/// Inspects the pallet's state and enumerates tasks of this type.
|
||||||
|
fn iter() -> Self::Enumeration;
|
||||||
|
|
||||||
|
/// Checks if a particular instance of this `Task` variant is a valid piece of work.
|
||||||
|
fn is_valid(&self) -> bool;
|
||||||
|
|
||||||
|
/// Performs the work for this particular `Task` variant.
|
||||||
|
fn run(&self) -> Result<(), DispatchError>;
|
||||||
|
|
||||||
|
/// Returns the weight of executing this `Task`.
|
||||||
|
fn weight(&self) -> Weight;
|
||||||
|
|
||||||
|
/// A unique value representing this `Task` within the current pallet. Analogous to
|
||||||
|
/// `call_index`, but for tasks.'
|
||||||
|
///
|
||||||
|
/// This value should be unique within the current pallet and can overlap with task indices
|
||||||
|
/// in other pallets.
|
||||||
|
fn task_index(&self) -> u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Task for () {
|
||||||
|
type Enumeration = IntoIter<Self>;
|
||||||
|
|
||||||
|
fn iter() -> Self::Enumeration {
|
||||||
|
vec![].into_iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_valid(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&self) -> Result<(), DispatchError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn weight(&self) -> Weight {
|
||||||
|
Weight::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn task_index(&self) -> u32 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
use renamed_frame_support::{
|
use renamed_frame_support::{
|
||||||
construct_runtime, parameter_types,
|
construct_runtime, derive_impl, parameter_types,
|
||||||
traits::{ConstU16, ConstU32, ConstU64, Everything},
|
traits::{ConstU16, ConstU32, ConstU64, Everything},
|
||||||
};
|
};
|
||||||
use sp_core::{sr25519, H256};
|
use sp_core::{sr25519, H256};
|
||||||
@@ -51,6 +51,7 @@ parameter_types! {
|
|||||||
pub const Version: RuntimeVersion = VERSION;
|
pub const Version: RuntimeVersion = VERSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive_impl(renamed_frame_system::config_preludes::TestDefaultConfig as renamed_frame_system::DefaultConfig)]
|
||||||
impl renamed_frame_system::Config for Runtime {
|
impl renamed_frame_system::Config for Runtime {
|
||||||
type BaseCallFilter = Everything;
|
type BaseCallFilter = Everything;
|
||||||
type BlockWeights = ();
|
type BlockWeights = ();
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ pub mod pallet {
|
|||||||
+ From<RawOrigin<Self::AccountId>>;
|
+ From<RawOrigin<Self::AccountId>>;
|
||||||
/// The runtime call type.
|
/// The runtime call type.
|
||||||
type RuntimeCall;
|
type RuntimeCall;
|
||||||
|
/// Contains an aggregation of all tasks in this runtime.
|
||||||
|
type RuntimeTask;
|
||||||
/// The runtime event type.
|
/// The runtime event type.
|
||||||
type RuntimeEvent: Parameter
|
type RuntimeEvent: Parameter
|
||||||
+ Member
|
+ Member
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
error: `Call` is not allowed to have generics. Only the following pallets are allowed to have generics: `Event`, `Error`, `Origin`, `Config`.
|
error: `Call` is not allowed to have generics. Only the following pallets are allowed to have generics: `Event`, `Error`, `Origin`, `Config`, `Task`.
|
||||||
--> tests/construct_runtime_ui/generics_in_invalid_module.rs:24:36
|
--> tests/construct_runtime_ui/generics_in_invalid_module.rs:24:36
|
||||||
|
|
|
|
||||||
24 | Balance: balances::<Instance1>::{Call<T>, Origin<T>},
|
24 | Balance: balances::<Instance1>::{Call<T>, Origin<T>},
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
error: expected one of: `Pallet`, `Call`, `Storage`, `Event`, `Error`, `Config`, `Origin`, `Inherent`, `ValidateUnsigned`, `FreezeReason`, `HoldReason`, `LockId`, `SlashReason`
|
error: expected one of: `Pallet`, `Call`, `Storage`, `Event`, `Error`, `Config`, `Origin`, `Inherent`, `ValidateUnsigned`, `FreezeReason`, `HoldReason`, `Task`, `LockId`, `SlashReason`
|
||||||
--> tests/construct_runtime_ui/invalid_module_details_keyword.rs:23:20
|
--> tests/construct_runtime_ui/invalid_module_details_keyword.rs:23:20
|
||||||
|
|
|
|
||||||
23 | system: System::{enum},
|
23 | system: System::{enum},
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
error: expected one of: `Pallet`, `Call`, `Storage`, `Event`, `Error`, `Config`, `Origin`, `Inherent`, `ValidateUnsigned`, `FreezeReason`, `HoldReason`, `LockId`, `SlashReason`
|
error: expected one of: `Pallet`, `Call`, `Storage`, `Event`, `Error`, `Config`, `Origin`, `Inherent`, `ValidateUnsigned`, `FreezeReason`, `HoldReason`, `Task`, `LockId`, `SlashReason`
|
||||||
--> tests/construct_runtime_ui/invalid_module_entry.rs:24:23
|
--> tests/construct_runtime_ui/invalid_module_entry.rs:24:23
|
||||||
|
|
|
|
||||||
24 | Balance: balances::{Unexpected},
|
24 | Balance: balances::{Unexpected},
|
||||||
|
|||||||
+8
@@ -76,3 +76,11 @@ help: consider importing one of these items
|
|||||||
|
|
|
|
||||||
18 + use frame_support::traits::PalletInfo;
|
18 + use frame_support::traits::PalletInfo;
|
||||||
|
|
|
|
||||||
|
|
||||||
|
error[E0412]: cannot find type `RuntimeTask` in this scope
|
||||||
|
--> tests/construct_runtime_ui/number_of_pallets_exceeds_tuple_size.rs:39:1
|
||||||
|
|
|
||||||
|
39 | #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you might have meant to use the associated type: `Self::RuntimeTask`
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `frame_system::config_preludes::TestDefaultConfig` which comes from the expansion of the macro `frame_support::macro_magic::forward_tokens_verbatim` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
error: `#[inject_runtime_type]` can only be attached to `RuntimeCall`, `RuntimeEvent`, `RuntimeOrigin` or `PalletInfo`
|
error: `#[inject_runtime_type]` can only be attached to `RuntimeCall`, `RuntimeEvent`, `RuntimeTask`, `RuntimeOrigin` or `PalletInfo`
|
||||||
--> tests/derive_impl_ui/inject_runtime_type_invalid.rs:32:5
|
--> tests/derive_impl_ui/inject_runtime_type_invalid.rs:32:5
|
||||||
|
|
|
|
||||||
32 | type RuntimeInfo = ();
|
32 | type RuntimeInfo = ();
|
||||||
|
|||||||
@@ -90,6 +90,8 @@ fn module_error_outer_enum_expand_explicit() {
|
|||||||
frame_system::Error::NonDefaultComposite => (),
|
frame_system::Error::NonDefaultComposite => (),
|
||||||
frame_system::Error::NonZeroRefCount => (),
|
frame_system::Error::NonZeroRefCount => (),
|
||||||
frame_system::Error::CallFiltered => (),
|
frame_system::Error::CallFiltered => (),
|
||||||
|
frame_system::Error::InvalidTask => (),
|
||||||
|
frame_system::Error::FailedTask => (),
|
||||||
frame_system::Error::__Ignore(_, _) => (),
|
frame_system::Error::__Ignore(_, _) => (),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -90,6 +90,8 @@ fn module_error_outer_enum_expand_implicit() {
|
|||||||
frame_system::Error::NonDefaultComposite => (),
|
frame_system::Error::NonDefaultComposite => (),
|
||||||
frame_system::Error::NonZeroRefCount => (),
|
frame_system::Error::NonZeroRefCount => (),
|
||||||
frame_system::Error::CallFiltered => (),
|
frame_system::Error::CallFiltered => (),
|
||||||
|
frame_system::Error::InvalidTask => (),
|
||||||
|
frame_system::Error::FailedTask => (),
|
||||||
frame_system::Error::__Ignore(_, _) => (),
|
frame_system::Error::__Ignore(_, _) => (),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
error: expected one of: `FreezeReason`, `HoldReason`, `LockId`, `SlashReason`
|
error: expected one of: `FreezeReason`, `HoldReason`, `LockId`, `SlashReason`, `Task`
|
||||||
--> tests/pallet_ui/composite_enum_unsupported_identifier.rs:27:11
|
--> tests/pallet_ui/composite_enum_unsupported_identifier.rs:27:11
|
||||||
|
|
|
|
||||||
27 | pub enum HoldReasons {}
|
27 | pub enum HoldReasons {}
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#[frame_support::pallet(dev_mode)]
|
||||||
|
mod pallet {
|
||||||
|
use frame_support::{ensure, pallet_prelude::DispatchResult};
|
||||||
|
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_system::Config {}
|
||||||
|
|
||||||
|
#[pallet::pallet]
|
||||||
|
pub struct Pallet<T>(core::marker::PhantomData<T>);
|
||||||
|
|
||||||
|
#[pallet::tasks_experimental]
|
||||||
|
impl<T: Config> Pallet<T> {
|
||||||
|
#[pallet::task_index(0)]
|
||||||
|
#[pallet::task_condition(|i, j| i == 0u32 && j == 2u64)]
|
||||||
|
#[pallet::task_list(vec![(0u32, 2u64), (2u32, 4u64)].iter())]
|
||||||
|
#[pallet::task_weight(0.into())]
|
||||||
|
fn foo(i: u32, j: u64) -> DispatchResult {
|
||||||
|
ensure!(i == 0, "i must be 0");
|
||||||
|
ensure!(j == 2, "j must be 2");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#[frame_support::pallet(dev_mode)]
|
||||||
|
mod pallet {
|
||||||
|
use frame_support::pallet_prelude::DispatchResult;
|
||||||
|
use frame_system::pallet_prelude::OriginFor;
|
||||||
|
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_system::Config {}
|
||||||
|
|
||||||
|
#[pallet::pallet]
|
||||||
|
pub struct Pallet<T>(core::marker::PhantomData<T>);
|
||||||
|
|
||||||
|
#[pallet::tasks_experimental]
|
||||||
|
pub struct Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
error: expected `impl`
|
||||||
|
--> tests/pallet_ui/task_can_only_be_attached_to_impl.rs:30:5
|
||||||
|
|
|
||||||
|
30 | pub struct Task;
|
||||||
|
| ^^^
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#[frame_support::pallet(dev_mode)]
|
||||||
|
mod pallet {
|
||||||
|
use frame_support::pallet_prelude::DispatchResult;
|
||||||
|
use frame_system::pallet_prelude::OriginFor;
|
||||||
|
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_system::Config {}
|
||||||
|
|
||||||
|
#[pallet::pallet]
|
||||||
|
pub struct Pallet<T>(core::marker::PhantomData<T>);
|
||||||
|
|
||||||
|
#[pallet::tasks_experimental]
|
||||||
|
impl<T: Config> Pallet<T> {
|
||||||
|
#[pallet::task_index(0)]
|
||||||
|
#[pallet::task_condition(|flag: bool| flag)]
|
||||||
|
#[pallet::task_list(vec![1, 2].iter())]
|
||||||
|
#[pallet::task_weight(0.into())]
|
||||||
|
fn foo(_i: u32) -> DispatchResult {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
error: unused import: `frame_system::pallet_prelude::OriginFor`
|
||||||
|
--> tests/pallet_ui/task_condition_invalid_arg.rs:21:6
|
||||||
|
|
|
||||||
|
21 | use frame_system::pallet_prelude::OriginFor;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `-D unused-imports` implied by `-D warnings`
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> tests/pallet_ui/task_condition_invalid_arg.rs:35:10
|
||||||
|
|
|
||||||
|
32 | #[pallet::task_condition(|flag: bool| flag)]
|
||||||
|
| ----------------- arguments to this function are incorrect
|
||||||
|
...
|
||||||
|
35 | fn foo(_i: u32) -> DispatchResult {
|
||||||
|
| ^^ expected `bool`, found `u32`
|
||||||
|
|
|
||||||
|
note: closure parameter defined here
|
||||||
|
--> tests/pallet_ui/task_condition_invalid_arg.rs:32:29
|
||||||
|
|
|
||||||
|
32 | #[pallet::task_condition(|flag: bool| flag)]
|
||||||
|
| ^^^^^^^^^^
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#[frame_support::pallet(dev_mode)]
|
||||||
|
mod pallet {
|
||||||
|
use frame_support::pallet_prelude::DispatchResult;
|
||||||
|
use frame_system::pallet_prelude::OriginFor;
|
||||||
|
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_system::Config {}
|
||||||
|
|
||||||
|
#[pallet::pallet]
|
||||||
|
pub struct Pallet<T>(core::marker::PhantomData<T>);
|
||||||
|
|
||||||
|
#[pallet::tasks_experimental]
|
||||||
|
impl<T: Config> Pallet<T> {
|
||||||
|
#[pallet::task_index(0)]
|
||||||
|
#[pallet::task_condition(0)]
|
||||||
|
#[pallet::task_list(vec![1, 2].iter())]
|
||||||
|
#[pallet::task_weight(0.into())]
|
||||||
|
fn foo() -> DispatchResult {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
error: unused import: `frame_system::pallet_prelude::OriginFor`
|
||||||
|
--> tests/pallet_ui/task_invalid_condition.rs:21:6
|
||||||
|
|
|
||||||
|
21 | use frame_system::pallet_prelude::OriginFor;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `-D unused-imports` implied by `-D warnings`
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> tests/pallet_ui/task_invalid_condition.rs:18:1
|
||||||
|
|
|
||||||
|
18 | #[frame_support::pallet(dev_mode)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
| |
|
||||||
|
| expected integer, found `()`
|
||||||
|
| expected due to this
|
||||||
|
|
|
||||||
|
= note: this error originates in the attribute macro `frame_support::pallet` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error[E0618]: expected function, found `{integer}`
|
||||||
|
--> tests/pallet_ui/task_invalid_condition.rs:32:28
|
||||||
|
|
|
||||||
|
18 | #[frame_support::pallet(dev_mode)]
|
||||||
|
| ---------------------------------- call expression requires function
|
||||||
|
...
|
||||||
|
32 | #[pallet::task_condition(0)]
|
||||||
|
| ^
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#[frame_support::pallet(dev_mode)]
|
||||||
|
mod pallet {
|
||||||
|
use frame_support::pallet_prelude::DispatchResult;
|
||||||
|
use frame_system::pallet_prelude::OriginFor;
|
||||||
|
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_system::Config {}
|
||||||
|
|
||||||
|
#[pallet::pallet]
|
||||||
|
pub struct Pallet<T>(core::marker::PhantomData<T>);
|
||||||
|
|
||||||
|
#[pallet::tasks_experimental]
|
||||||
|
impl<T: Config> Pallet<T> {
|
||||||
|
#[pallet::task_index("0")]
|
||||||
|
fn foo() -> DispatchResult {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
error: expected integer literal
|
||||||
|
--> tests/pallet_ui/task_invalid_index.rs:31:24
|
||||||
|
|
|
||||||
|
31 | #[pallet::task_index("0")]
|
||||||
|
| ^^^
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#[frame_support::pallet(dev_mode)]
|
||||||
|
mod pallet {
|
||||||
|
use frame_support::pallet_prelude::DispatchResult;
|
||||||
|
use frame_system::pallet_prelude::OriginFor;
|
||||||
|
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_system::Config {}
|
||||||
|
|
||||||
|
#[pallet::pallet]
|
||||||
|
pub struct Pallet<T>(core::marker::PhantomData<T>);
|
||||||
|
|
||||||
|
#[pallet::tasks_experimental]
|
||||||
|
impl<T: Config> Pallet<T> {
|
||||||
|
#[pallet::task_index(0)]
|
||||||
|
#[pallet::task_condition(|| true)]
|
||||||
|
#[pallet::task_list(0)]
|
||||||
|
#[pallet::task_weight(0.into())]
|
||||||
|
fn foo() -> DispatchResult {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
error: unused import: `frame_system::pallet_prelude::OriginFor`
|
||||||
|
--> tests/pallet_ui/task_invalid_list.rs:21:6
|
||||||
|
|
|
||||||
|
21 | use frame_system::pallet_prelude::OriginFor;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `-D unused-imports` implied by `-D warnings`
|
||||||
|
|
||||||
|
error[E0689]: can't call method `map` on ambiguous numeric type `{integer}`
|
||||||
|
--> tests/pallet_ui/task_invalid_list.rs:18:1
|
||||||
|
|
|
||||||
|
18 | #[frame_support::pallet(dev_mode)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: this error originates in the attribute macro `frame_support::pallet` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
help: you must specify a concrete type for this numeric value, like `i32`
|
||||||
|
|
|
||||||
|
33 | #[pallet::task_list(0_i32)]
|
||||||
|
| ~~~~~
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#[frame_support::pallet(dev_mode)]
|
||||||
|
mod pallet {
|
||||||
|
use frame_support::pallet_prelude::DispatchResult;
|
||||||
|
use frame_system::pallet_prelude::OriginFor;
|
||||||
|
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_system::Config {}
|
||||||
|
|
||||||
|
#[pallet::pallet]
|
||||||
|
pub struct Pallet<T>(core::marker::PhantomData<T>);
|
||||||
|
|
||||||
|
#[pallet::tasks_experimental]
|
||||||
|
impl<T: Config> Pallet<T> {
|
||||||
|
#[pallet::task_index(0)]
|
||||||
|
#[pallet::task_condition(|| true)]
|
||||||
|
#[pallet::task_list(vec![1, 2].iter())]
|
||||||
|
#[pallet::task_weight("0")]
|
||||||
|
fn foo() -> DispatchResult {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
error: unused import: `frame_system::pallet_prelude::OriginFor`
|
||||||
|
--> tests/pallet_ui/task_invalid_weight.rs:21:6
|
||||||
|
|
|
||||||
|
21 | use frame_system::pallet_prelude::OriginFor;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `-D unused-imports` implied by `-D warnings`
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> tests/pallet_ui/task_invalid_weight.rs:18:1
|
||||||
|
|
|
||||||
|
18 | #[frame_support::pallet(dev_mode)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
| |
|
||||||
|
| expected integer, found `()`
|
||||||
|
| expected due to this
|
||||||
|
|
|
||||||
|
= note: this error originates in the attribute macro `frame_support::pallet` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> tests/pallet_ui/task_invalid_weight.rs:34:25
|
||||||
|
|
|
||||||
|
18 | #[frame_support::pallet(dev_mode)]
|
||||||
|
| ---------------------------------- expected `Weight` because of return type
|
||||||
|
...
|
||||||
|
34 | #[pallet::task_weight("0")]
|
||||||
|
| ^^^ expected `Weight`, found `&str`
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#[frame_support::pallet(dev_mode)]
|
||||||
|
mod pallet {
|
||||||
|
use frame_support::pallet_prelude::DispatchResult;
|
||||||
|
use frame_system::pallet_prelude::OriginFor;
|
||||||
|
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_system::Config {}
|
||||||
|
|
||||||
|
#[pallet::pallet]
|
||||||
|
pub struct Pallet<T>(core::marker::PhantomData<T>);
|
||||||
|
|
||||||
|
#[pallet::tasks_experimental]
|
||||||
|
impl<T: Config> Pallet<T> {
|
||||||
|
#[pallet::task_index(0)]
|
||||||
|
fn foo() -> DispatchResult {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
error: missing `#[pallet::task_condition(..)]` attribute
|
||||||
|
--> tests/pallet_ui/task_missing_condition.rs:32:6
|
||||||
|
|
|
||||||
|
32 | fn foo() -> DispatchResult {
|
||||||
|
| ^^^
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#[frame_support::pallet(dev_mode)]
|
||||||
|
mod pallet {
|
||||||
|
use frame_support::pallet_prelude::DispatchResult;
|
||||||
|
use frame_system::pallet_prelude::OriginFor;
|
||||||
|
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_system::Config {}
|
||||||
|
|
||||||
|
#[pallet::pallet]
|
||||||
|
pub struct Pallet<T>(core::marker::PhantomData<T>);
|
||||||
|
|
||||||
|
#[pallet::tasks_experimental]
|
||||||
|
impl<T: Config> Pallet<T> {
|
||||||
|
fn foo() -> DispatchResult {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
error: missing `#[pallet::task_index(..)]` attribute
|
||||||
|
--> tests/pallet_ui/task_missing_index.rs:31:6
|
||||||
|
|
|
||||||
|
31 | fn foo() -> DispatchResult {
|
||||||
|
| ^^^
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#[frame_support::pallet(dev_mode)]
|
||||||
|
mod pallet {
|
||||||
|
use frame_support::pallet_prelude::DispatchResult;
|
||||||
|
use frame_system::pallet_prelude::OriginFor;
|
||||||
|
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_system::Config {}
|
||||||
|
|
||||||
|
#[pallet::pallet]
|
||||||
|
pub struct Pallet<T>(core::marker::PhantomData<T>);
|
||||||
|
|
||||||
|
#[pallet::tasks_experimental]
|
||||||
|
impl<T: Config> Pallet<T> {
|
||||||
|
#[pallet::task_index(0)]
|
||||||
|
#[pallet::task_condition(|| true)]
|
||||||
|
fn foo() -> DispatchResult {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
error: missing `#[pallet::task_list(..)]` attribute
|
||||||
|
--> tests/pallet_ui/task_missing_list.rs:33:6
|
||||||
|
|
|
||||||
|
33 | fn foo() -> DispatchResult {
|
||||||
|
| ^^^
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#[frame_support::pallet(dev_mode)]
|
||||||
|
mod pallet {
|
||||||
|
use frame_support::pallet_prelude::DispatchResult;
|
||||||
|
use frame_system::pallet_prelude::OriginFor;
|
||||||
|
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_system::Config {}
|
||||||
|
|
||||||
|
#[pallet::pallet]
|
||||||
|
pub struct Pallet<T>(core::marker::PhantomData<T>);
|
||||||
|
|
||||||
|
#[pallet::tasks_experimental]
|
||||||
|
impl<T: Config> Pallet<T> {
|
||||||
|
#[pallet::task_index(0)]
|
||||||
|
#[pallet::task_condition(|| true)]
|
||||||
|
#[pallet::task_list(vec![1, 2].iter())]
|
||||||
|
fn foo() -> DispatchResult {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
error: missing `#[pallet::task_weight(..)]` attribute
|
||||||
|
--> tests/pallet_ui/task_missing_weight.rs:34:6
|
||||||
|
|
|
||||||
|
34 | fn foo() -> DispatchResult {
|
||||||
|
| ^^^
|
||||||
@@ -241,6 +241,8 @@ pub mod pallet {
|
|||||||
type RuntimeCall = ();
|
type RuntimeCall = ();
|
||||||
#[inject_runtime_type]
|
#[inject_runtime_type]
|
||||||
type PalletInfo = ();
|
type PalletInfo = ();
|
||||||
|
#[inject_runtime_type]
|
||||||
|
type RuntimeTask = ();
|
||||||
type BaseCallFilter = frame_support::traits::Everything;
|
type BaseCallFilter = frame_support::traits::Everything;
|
||||||
type BlockHashCount = frame_support::traits::ConstU64<10>;
|
type BlockHashCount = frame_support::traits::ConstU64<10>;
|
||||||
type OnSetCode = ();
|
type OnSetCode = ();
|
||||||
@@ -323,6 +325,8 @@ pub mod pallet {
|
|||||||
|
|
||||||
/// Converts a module to the index of the module, injected by `construct_runtime!`.
|
/// Converts a module to the index of the module, injected by `construct_runtime!`.
|
||||||
#[inject_runtime_type]
|
#[inject_runtime_type]
|
||||||
|
type RuntimeTask = ();
|
||||||
|
#[inject_runtime_type]
|
||||||
type PalletInfo = ();
|
type PalletInfo = ();
|
||||||
|
|
||||||
/// The basic call filter to use in dispatchable. Supports everything as the default.
|
/// The basic call filter to use in dispatchable. Supports everything as the default.
|
||||||
@@ -400,6 +404,10 @@ pub mod pallet {
|
|||||||
+ Debug
|
+ Debug
|
||||||
+ From<Call<Self>>;
|
+ From<Call<Self>>;
|
||||||
|
|
||||||
|
/// The aggregated `RuntimeTask` type.
|
||||||
|
#[pallet::no_default_bounds]
|
||||||
|
type RuntimeTask: Task;
|
||||||
|
|
||||||
/// This stores the number of previous transactions associated with a sender account.
|
/// This stores the number of previous transactions associated with a sender account.
|
||||||
type Nonce: Parameter
|
type Nonce: Parameter
|
||||||
+ Member
|
+ Member
|
||||||
@@ -628,6 +636,28 @@ pub mod pallet {
|
|||||||
Self::deposit_event(Event::Remarked { sender: who, hash });
|
Self::deposit_event(Event::Remarked { sender: who, hash });
|
||||||
Ok(().into())
|
Ok(().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[pallet::call_index(8)]
|
||||||
|
#[pallet::weight(task.weight())]
|
||||||
|
pub fn do_task(origin: OriginFor<T>, task: T::RuntimeTask) -> DispatchResultWithPostInfo {
|
||||||
|
ensure_signed(origin)?;
|
||||||
|
|
||||||
|
if !task.is_valid() {
|
||||||
|
return Err(Error::<T>::InvalidTask.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::deposit_event(Event::TaskStarted { task: task.clone() });
|
||||||
|
if let Err(err) = task.run() {
|
||||||
|
Self::deposit_event(Event::TaskFailed { task, err });
|
||||||
|
return Err(Error::<T>::FailedTask.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit a success event, if your design includes events for this pallet.
|
||||||
|
Self::deposit_event(Event::TaskCompleted { task });
|
||||||
|
|
||||||
|
// Return success.
|
||||||
|
Ok(().into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Event for the System pallet.
|
/// Event for the System pallet.
|
||||||
@@ -645,6 +675,12 @@ pub mod pallet {
|
|||||||
KilledAccount { account: T::AccountId },
|
KilledAccount { account: T::AccountId },
|
||||||
/// On on-chain remark happened.
|
/// On on-chain remark happened.
|
||||||
Remarked { sender: T::AccountId, hash: T::Hash },
|
Remarked { sender: T::AccountId, hash: T::Hash },
|
||||||
|
/// A [`Task`] has started executing
|
||||||
|
TaskStarted { task: T::RuntimeTask },
|
||||||
|
/// A [`Task`] has finished executing.
|
||||||
|
TaskCompleted { task: T::RuntimeTask },
|
||||||
|
/// A [`Task`] failed during execution.
|
||||||
|
TaskFailed { task: T::RuntimeTask, err: DispatchError },
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Error for the System pallet
|
/// Error for the System pallet
|
||||||
@@ -666,6 +702,10 @@ pub mod pallet {
|
|||||||
NonZeroRefCount,
|
NonZeroRefCount,
|
||||||
/// The origin filter prevent the call to be dispatched.
|
/// The origin filter prevent the call to be dispatched.
|
||||||
CallFiltered,
|
CallFiltered,
|
||||||
|
/// The specified [`Task`] is not valid.
|
||||||
|
InvalidTask,
|
||||||
|
/// The specified [`Task`] failed during execution.
|
||||||
|
FailedTask,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Exposed trait-generic origin type.
|
/// Exposed trait-generic origin type.
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
use crate::{self as frame_system, *};
|
use crate::{self as frame_system, *};
|
||||||
use frame_support::{
|
use frame_support::{
|
||||||
parameter_types,
|
derive_impl, parameter_types,
|
||||||
traits::{ConstU32, ConstU64},
|
traits::{ConstU32, ConstU64},
|
||||||
};
|
};
|
||||||
use sp_core::H256;
|
use sp_core::H256;
|
||||||
@@ -85,6 +85,7 @@ impl OnKilledAccount<u64> for RecordKilled {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
|
||||||
impl Config for Test {
|
impl Config for Test {
|
||||||
type BaseCallFilter = frame_support::traits::Everything;
|
type BaseCallFilter = frame_support::traits::Everything;
|
||||||
type BlockWeights = RuntimeBlockWeights;
|
type BlockWeights = RuntimeBlockWeights;
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ pub mod substrate_test_pallet;
|
|||||||
|
|
||||||
use codec::{Decode, Encode};
|
use codec::{Decode, Encode};
|
||||||
use frame_support::{
|
use frame_support::{
|
||||||
construct_runtime,
|
construct_runtime, derive_impl,
|
||||||
dispatch::DispatchClass,
|
dispatch::DispatchClass,
|
||||||
genesis_builder_helper::{build_config, create_default_config},
|
genesis_builder_helper::{build_config, create_default_config},
|
||||||
parameter_types,
|
parameter_types,
|
||||||
@@ -342,6 +342,7 @@ parameter_types! {
|
|||||||
.build_or_panic();
|
.build_or_panic();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
|
||||||
impl frame_system::pallet::Config for Runtime {
|
impl frame_system::pallet::Config for Runtime {
|
||||||
type BaseCallFilter = frame_support::traits::Everything;
|
type BaseCallFilter = frame_support::traits::Everything;
|
||||||
type BlockWeights = RuntimeBlockWeights;
|
type BlockWeights = RuntimeBlockWeights;
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ use sp_storage::{StorageData, StorageKey};
|
|||||||
/// # type Lookup = IdentityLookup<Self::AccountId>;
|
/// # type Lookup = IdentityLookup<Self::AccountId>;
|
||||||
/// # type Block = frame_system::mocking::MockBlock<TestRuntime>;
|
/// # type Block = frame_system::mocking::MockBlock<TestRuntime>;
|
||||||
/// # type RuntimeEvent = RuntimeEvent;
|
/// # type RuntimeEvent = RuntimeEvent;
|
||||||
|
/// # type RuntimeTask = RuntimeTask;
|
||||||
/// # type BlockHashCount = ();
|
/// # type BlockHashCount = ();
|
||||||
/// # type DbWeight = ();
|
/// # type DbWeight = ();
|
||||||
/// # type Version = ();
|
/// # type Version = ();
|
||||||
|
|||||||
Reference in New Issue
Block a user