Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
Extend PalletInfoAccess with module_name and crate_version method (#9690
Browse files Browse the repository at this point in the history
)

* Record pallet indices in CallMetadata

* Resurrect PalletVersion infrastructure and rename as CrateVersion

* cargo fmt

* Add missing runtime generics to pallet struct

* Fix path to instance

* Fix test

* Fix UI test expectations

* Fix UI test expectations

* Move crate_version function to PalletInfoAccess

* Update UI test expectations

* Add crate_name method to PalletInfo

* Convert path to module name instead of exposing crate name

* cargo fmt

* Keep the double colons when constructing the module name

* Remove unused import

* Update UI test expectations

* Update frame/support/src/traits/metadata.rs

Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com>

* Update UI test expectations

Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com>
  • Loading branch information
KiChjang and thiolliere committed Sep 28, 2021
1 parent fa92eaf commit c11a0bf
Show file tree
Hide file tree
Showing 13 changed files with 299 additions and 16 deletions.
43 changes: 39 additions & 4 deletions frame/support/procedural/src/construct_runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ fn construct_runtime_parsed(definition: RuntimeDefinition) -> Result<TokenStream

let outer_origin = expand::expand_outer_origin(&name, &pallets, pallets_token, &scrate)?;
let all_pallets = decl_all_pallets(&name, pallets.iter());
let pallet_to_index = decl_pallet_runtime_setup(&pallets, &scrate);
let pallet_to_index = decl_pallet_runtime_setup(&name, &pallets, &scrate);

let dispatch = expand::expand_outer_dispatch(&name, &pallets, &scrate);
let metadata = expand::expand_runtime_metadata(&name, &pallets, &scrate, &unchecked_extrinsic);
Expand Down Expand Up @@ -236,13 +236,24 @@ fn decl_all_pallets<'a>(
}

fn decl_pallet_runtime_setup(
runtime: &Ident,
pallet_declarations: &[Pallet],
scrate: &TokenStream2,
) -> TokenStream2 {
let names = pallet_declarations.iter().map(|d| &d.name);
let names2 = pallet_declarations.iter().map(|d| &d.name);
let names = pallet_declarations.iter().map(|d| &d.name).collect::<Vec<_>>();
let name_strings = pallet_declarations.iter().map(|d| d.name.to_string());
let module_names = pallet_declarations.iter().map(|d| d.path.module_name());
let indices = pallet_declarations.iter().map(|pallet| pallet.index as usize);
let pallet_structs = pallet_declarations
.iter()
.map(|pallet| {
let path = &pallet.path;
match pallet.instance.as_ref() {
Some(inst) => quote!(#path::Pallet<#runtime, #path::#inst>),
None => quote!(#path::Pallet<#runtime>),
}
})
.collect::<Vec<_>>();

quote!(
/// Provides an implementation of `PalletInfo` to provide information
Expand All @@ -264,13 +275,37 @@ fn decl_pallet_runtime_setup(
fn name<P: 'static>() -> Option<&'static str> {
let type_id = #scrate::sp_std::any::TypeId::of::<P>();
#(
if type_id == #scrate::sp_std::any::TypeId::of::<#names2>() {
if type_id == #scrate::sp_std::any::TypeId::of::<#names>() {
return Some(#name_strings)
}
)*

None
}

fn module_name<P: 'static>() -> Option<&'static str> {
let type_id = #scrate::sp_std::any::TypeId::of::<P>();
#(
if type_id == #scrate::sp_std::any::TypeId::of::<#names>() {
return Some(#module_names)
}
)*

None
}

fn crate_version<P: 'static>() -> Option<#scrate::traits::CrateVersion> {
let type_id = #scrate::sp_std::any::TypeId::of::<P>();
#(
if type_id == #scrate::sp_std::any::TypeId::of::<#names>() {
return Some(
<#pallet_structs as #scrate::traits::PalletInfoAccess>::crate_version()
)
}
)*

None
}
}
)
}
Expand Down
12 changes: 12 additions & 0 deletions frame/support/procedural/src/construct_runtime/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,18 @@ pub struct PalletPath {
pub inner: Path,
}

impl PalletPath {
pub fn module_name(&self) -> String {
self.inner.segments.iter().fold(String::new(), |mut acc, segment| {
if !acc.is_empty() {
acc.push_str("::");
}
acc.push_str(&segment.ident.to_string());
acc
})
}
}

impl Parse for PalletPath {
fn parse(input: ParseStream) -> Result<Self> {
let mut lookahead = input.lookahead1();
Expand Down
54 changes: 54 additions & 0 deletions frame/support/procedural/src/crate_version.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// This file is part of Substrate.

// Copyright (C) 2020-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Implementation of macros related to crate versioning.

use super::get_cargo_env_var;
use frame_support_procedural_tools::generate_crate_access_2018;
use proc_macro2::{Span, TokenStream};
use syn::{Error, Result};

/// Create an error that will be shown by rustc at the call site of the macro.
fn create_error(message: &str) -> Error {
Error::new(Span::call_site(), message)
}

/// Implementation of the `crate_to_crate_version!` macro.
pub fn crate_to_crate_version(input: proc_macro::TokenStream) -> Result<TokenStream> {
if !input.is_empty() {
return Err(create_error("No arguments expected!"))
}

let major_version = get_cargo_env_var::<u16>("CARGO_PKG_VERSION_MAJOR")
.map_err(|_| create_error("Major version needs to fit into `u16`"))?;

let minor_version = get_cargo_env_var::<u8>("CARGO_PKG_VERSION_MINOR")
.map_err(|_| create_error("Minor version needs to fit into `u8`"))?;

let patch_version = get_cargo_env_var::<u8>("CARGO_PKG_VERSION_PATCH")
.map_err(|_| create_error("Patch version needs to fit into `u8`"))?;

let crate_ = generate_crate_access_2018("frame-support")?;

Ok(quote::quote! {
#crate_::traits::CrateVersion {
major: #major_version,
minor: #minor_version,
patch: #patch_version,
}
})
}
20 changes: 19 additions & 1 deletion frame/support/procedural/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

mod clone_no_bound;
mod construct_runtime;
mod crate_version;
mod debug_no_bound;
mod default_no_bound;
mod dummy_part_checker;
Expand All @@ -31,7 +32,7 @@ mod storage;
mod transactional;

use proc_macro::TokenStream;
use std::cell::RefCell;
use std::{cell::RefCell, str::FromStr};
pub(crate) use storage::INHERENT_INSTANCE_NAME;

thread_local! {
Expand All @@ -52,6 +53,16 @@ impl Counter {
}
}

/// Get the value from the given environment variable set by cargo.
///
/// The value is parsed into the requested destination type.
fn get_cargo_env_var<T: FromStr>(version_env: &str) -> std::result::Result<T, ()> {
let version = std::env::var(version_env)
.unwrap_or_else(|_| panic!("`{}` is always set by cargo; qed", version_env));

T::from_str(&version).map_err(drop)
}

/// Declares strongly-typed wrappers around codec-compatible types in storage.
///
/// ## Example
Expand Down Expand Up @@ -462,6 +473,13 @@ pub fn require_transactional(attr: TokenStream, input: TokenStream) -> TokenStre
.unwrap_or_else(|e| e.to_compile_error().into())
}

#[proc_macro]
pub fn crate_to_crate_version(input: TokenStream) -> TokenStream {
crate_version::crate_to_crate_version(input)
.unwrap_or_else(|e| e.to_compile_error())
.into()
}

/// The number of module instances supported by the runtime, starting at index 1,
/// and up to `NUMBER_OF_INSTANCE`.
pub(crate) const NUMBER_OF_INSTANCE: u8 = 16;
Expand Down
12 changes: 12 additions & 0 deletions frame/support/procedural/src/pallet/expand/pallet_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,18 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream {
.expect("Pallet is part of the runtime because pallet `Config` trait is \
implemented by the runtime")
}

fn module_name() -> &'static str {
<
<T as #frame_system::Config>::PalletInfo as #frame_support::traits::PalletInfo
>::module_name::<Self>()
.expect("Pallet is part of the runtime because pallet `Config` trait is \
implemented by the runtime")
}

fn crate_version() -> #frame_support::traits::CrateVersion {
#frame_support::crate_to_crate_version!()
}
}

#storage_info
Expand Down
32 changes: 30 additions & 2 deletions frame/support/src/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2151,6 +2151,18 @@ macro_rules! decl_module {
.expect("Pallet is part of the runtime because pallet `Config` trait is \
implemented by the runtime")
}

fn module_name() -> &'static str {
<
<$trait_instance as $system::Config>::PalletInfo as $crate::traits::PalletInfo
>::module_name::<Self>()
.expect("Pallet is part of the runtime because pallet `Config` trait is \
implemented by the runtime")
}

fn crate_version() -> $crate::traits::CrateVersion {
$crate::crate_to_crate_version!()
}
}

// Implement GetCallName for the Call.
Expand Down Expand Up @@ -2529,8 +2541,8 @@ mod tests {
use crate::{
metadata::*,
traits::{
Get, GetCallName, IntegrityTest, OnFinalize, OnIdle, OnInitialize, OnRuntimeUpgrade,
PalletInfo,
CrateVersion, Get, GetCallName, IntegrityTest, OnFinalize, OnIdle, OnInitialize,
OnRuntimeUpgrade, PalletInfo,
},
weights::{DispatchClass, DispatchInfo, Pays, RuntimeDbWeight},
};
Expand Down Expand Up @@ -2631,6 +2643,22 @@ mod tests {
return Some("Test")
}

None
}
fn module_name<P: 'static>() -> Option<&'static str> {
let type_id = sp_std::any::TypeId::of::<P>();
if type_id == sp_std::any::TypeId::of::<Test>() {
return Some("tests")
}

None
}
fn crate_version<P: 'static>() -> Option<CrateVersion> {
let type_id = sp_std::any::TypeId::of::<P>();
if type_id == sp_std::any::TypeId::of::<Test>() {
return Some(frame_support::crate_to_crate_version!())
}

None
}
}
Expand Down
22 changes: 22 additions & 0 deletions frame/support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,21 @@ pub use frame_support_procedural::DefaultNoBound;
/// ```
pub use frame_support_procedural::require_transactional;

/// Convert the current crate version into a [`CrateVersion`](crate::traits::CrateVersion).
///
/// It uses the `CARGO_PKG_VERSION_MAJOR`, `CARGO_PKG_VERSION_MINOR` and
/// `CARGO_PKG_VERSION_PATCH` environment variables to fetch the crate version.
/// This means that the [`CrateVersion`](crate::traits::CrateVersion)
/// object will correspond to the version of the crate the macro is called in!
///
/// # Example
///
/// ```
/// # use frame_support::{traits::CrateVersion, crate_to_crate_version};
/// const Version: CrateVersion = crate_to_crate_version!();
/// ```
pub use frame_support_procedural::crate_to_crate_version;

/// Return Err of the expression: `return Err($expression);`.
///
/// Used as `fail!(expression)`.
Expand Down Expand Up @@ -819,6 +834,7 @@ pub mod tests {
StorageHasher,
};
use codec::{Codec, EncodeLike};
use frame_support::traits::CrateVersion;
use sp_io::TestExternalities;
use sp_std::result;

Expand All @@ -832,6 +848,12 @@ pub mod tests {
fn name<P: 'static>() -> Option<&'static str> {
unimplemented!("PanicPalletInfo mustn't be triggered by tests");
}
fn module_name<P: 'static>() -> Option<&'static str> {
unimplemented!("PanicPalletInfo mustn't be triggered by tests");
}
fn crate_version<P: 'static>() -> Option<CrateVersion> {
unimplemented!("PanicPalletInfo mustn't be triggered by tests");
}
}

pub trait Config: 'static {
Expand Down
4 changes: 2 additions & 2 deletions frame/support/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ pub use randomness::Randomness;

mod metadata;
pub use metadata::{
CallMetadata, GetCallMetadata, GetCallName, GetStorageVersion, PalletInfo, PalletInfoAccess,
StorageVersion, STORAGE_VERSION_STORAGE_KEY_POSTFIX,
CallMetadata, CrateVersion, GetCallMetadata, GetCallName, GetStorageVersion, PalletInfo,
PalletInfoAccess, StorageVersion, STORAGE_VERSION_STORAGE_KEY_POSTFIX,
};

mod hooks;
Expand Down
43 changes: 40 additions & 3 deletions frame/support/src/traits/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
use codec::{Decode, Encode};
use sp_runtime::RuntimeDebug;

/// Provides information about the pallet setup in the runtime.
/// Provides information about the pallet itself and its setup in the runtime.
///
/// An implementor should be able to provide information about each pallet that
/// is configured in `construct_runtime!`.
Expand All @@ -29,16 +29,25 @@ pub trait PalletInfo {
fn index<P: 'static>() -> Option<usize>;
/// Convert the given pallet `P` into its name as configured in the runtime.
fn name<P: 'static>() -> Option<&'static str>;
/// Convert the given pallet `P` into its Rust module name as used in `construct_runtime!`.
fn module_name<P: 'static>() -> Option<&'static str>;
/// Convert the given pallet `P` into its containing crate version.
fn crate_version<P: 'static>() -> Option<CrateVersion>;
}

/// Provides information about the pallet setup in the runtime.
/// Provides information about the pallet itself and its setup in the runtime.
///
/// Access the information provided by [`PalletInfo`] for a specific pallet.
/// Declare some information and access the information provided by [`PalletInfo`] for a specific
/// pallet.
pub trait PalletInfoAccess {
/// Index of the pallet as configured in the runtime.
fn index() -> usize;
/// Name of the pallet as configured in the runtime.
fn name() -> &'static str;
/// Name of the Rust module containing the pallet.
fn module_name() -> &'static str;
/// Version of the crate containing the pallet.
fn crate_version() -> CrateVersion;
}

/// The function and pallet name of the Call.
Expand Down Expand Up @@ -68,6 +77,34 @@ pub trait GetCallMetadata {
fn get_call_metadata(&self) -> CallMetadata;
}

/// The version of a crate.
#[derive(RuntimeDebug, Eq, PartialEq, Encode, Decode, Ord, Clone, Copy, Default)]
pub struct CrateVersion {
/// The major version of the crate.
pub major: u16,
/// The minor version of the crate.
pub minor: u8,
/// The patch version of the crate.
pub patch: u8,
}

impl CrateVersion {
pub const fn new(major: u16, minor: u8, patch: u8) -> Self {
Self { major, minor, patch }
}
}

impl sp_std::cmp::PartialOrd for CrateVersion {
fn partial_cmp(&self, other: &Self) -> Option<sp_std::cmp::Ordering> {
let res = self
.major
.cmp(&other.major)
.then_with(|| self.minor.cmp(&other.minor).then_with(|| self.patch.cmp(&other.patch)));

Some(res)
}
}

/// The storage key postfix that is used to store the [`StorageVersion`] per pallet.
///
/// The full storage key is built by using:
Expand Down
Loading

0 comments on commit c11a0bf

Please sign in to comment.