mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-25 00:11:08 +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:
@@ -26,6 +26,7 @@ mod metadata;
|
||||
mod origin;
|
||||
mod outer_enums;
|
||||
mod slash_reason;
|
||||
mod task;
|
||||
mod unsigned;
|
||||
|
||||
pub use call::expand_outer_dispatch;
|
||||
@@ -38,4 +39,5 @@ pub use metadata::expand_runtime_metadata;
|
||||
pub use origin::expand_outer_origin;
|
||||
pub use outer_enums::{expand_outer_enum, OuterEnumType};
|
||||
pub use slash_reason::expand_outer_slash_reason;
|
||||
pub use task::expand_outer_task;
|
||||
pub use unsigned::expand_outer_validate_unsigned;
|
||||
|
||||
@@ -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 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(
|
||||
&name,
|
||||
&pallets,
|
||||
@@ -475,6 +476,8 @@ fn construct_runtime_final_expansion(
|
||||
|
||||
#dispatch
|
||||
|
||||
#tasks
|
||||
|
||||
#metadata
|
||||
|
||||
#outer_config
|
||||
|
||||
@@ -42,6 +42,7 @@ mod keyword {
|
||||
syn::custom_keyword!(ValidateUnsigned);
|
||||
syn::custom_keyword!(FreezeReason);
|
||||
syn::custom_keyword!(HoldReason);
|
||||
syn::custom_keyword!(Task);
|
||||
syn::custom_keyword!(LockId);
|
||||
syn::custom_keyword!(SlashReason);
|
||||
syn::custom_keyword!(exclude_parts);
|
||||
@@ -404,6 +405,7 @@ pub enum PalletPartKeyword {
|
||||
ValidateUnsigned(keyword::ValidateUnsigned),
|
||||
FreezeReason(keyword::FreezeReason),
|
||||
HoldReason(keyword::HoldReason),
|
||||
Task(keyword::Task),
|
||||
LockId(keyword::LockId),
|
||||
SlashReason(keyword::SlashReason),
|
||||
}
|
||||
@@ -434,6 +436,8 @@ impl Parse for PalletPartKeyword {
|
||||
Ok(Self::FreezeReason(input.parse()?))
|
||||
} else if lookahead.peek(keyword::HoldReason) {
|
||||
Ok(Self::HoldReason(input.parse()?))
|
||||
} else if lookahead.peek(keyword::Task) {
|
||||
Ok(Self::Task(input.parse()?))
|
||||
} else if lookahead.peek(keyword::LockId) {
|
||||
Ok(Self::LockId(input.parse()?))
|
||||
} else if lookahead.peek(keyword::SlashReason) {
|
||||
@@ -459,6 +463,7 @@ impl PalletPartKeyword {
|
||||
Self::ValidateUnsigned(_) => "ValidateUnsigned",
|
||||
Self::FreezeReason(_) => "FreezeReason",
|
||||
Self::HoldReason(_) => "HoldReason",
|
||||
Self::Task(_) => "Task",
|
||||
Self::LockId(_) => "LockId",
|
||||
Self::SlashReason(_) => "SlashReason",
|
||||
}
|
||||
@@ -471,7 +476,7 @@ impl PalletPartKeyword {
|
||||
|
||||
/// Returns the names of all pallet parts that allow to have a generic argument.
|
||||
fn all_generic_arg() -> &'static [&'static str] {
|
||||
&["Event", "Error", "Origin", "Config"]
|
||||
&["Event", "Error", "Origin", "Config", "Task"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -489,6 +494,7 @@ impl ToTokens for PalletPartKeyword {
|
||||
Self::ValidateUnsigned(inner) => inner.to_tokens(tokens),
|
||||
Self::FreezeReason(inner) => inner.to_tokens(tokens),
|
||||
Self::HoldReason(inner) => inner.to_tokens(tokens),
|
||||
Self::Task(inner) => inner.to_tokens(tokens),
|
||||
Self::LockId(inner) => inner.to_tokens(tokens),
|
||||
Self::SlashReason(inner) => inner.to_tokens(tokens),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user