Replace JSON metadata with custom metadata structures (#766)

* Move module metadata from json string to custom metadata

* Revisit the metadata structures

1. Move the structures into the metadata crate.
2. Switch to using Cow/MaybeOwnedArray to support Encode/Decode

* Adapt to new metadata structures

* Convert event json metadata to new metadata structures

* Convert storage json metadata to new metadata structures

* Convert runtime metadata from json to new metadata structs

* Implements new metadata structures in client and runtime

* Fixes errors on `no_std`

* Fixes errors after rebasing master

* Do not use `Cow` anymore in metadata

Also replace `String` with our own type definition `StringBuf`.
This fixes compilation on `no_std`.

* Wrap `RuntimeMetadata` in `RuntimeMetadataVersioned` to support versioning

* Move metadata into `srml` and make core unaware of the implementation
This commit is contained in:
Bastian Köcher
2018-09-20 07:35:32 +02:00
committed by Gav Wood
parent 3c0da110f3
commit 5d64be26c3
22 changed files with 1138 additions and 843 deletions
+3 -2
View File
@@ -2578,7 +2578,6 @@ dependencies = [
"pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-io 0.1.0",
"sr-std 0.1.0",
"substrate-metadata 0.1.0",
@@ -2742,7 +2741,6 @@ dependencies = [
"substrate-bft 0.1.0",
"substrate-executor 0.1.0",
"substrate-keyring 0.1.0",
"substrate-metadata 0.1.0",
"substrate-primitives 0.1.0",
"substrate-state-machine 0.1.0",
"substrate-telemetry 0.3.0",
@@ -2844,6 +2842,9 @@ name = "substrate-metadata"
version = "0.1.0"
dependencies = [
"parity-codec 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec-derive 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
+1 -1
View File
@@ -28,7 +28,6 @@ members = [
"core/executor",
"core/extrinsic-pool",
"core/keyring",
"core/metadata",
"core/misbehavior-check",
"core/network",
"core/primitives",
@@ -46,6 +45,7 @@ members = [
"srml/democracy",
"srml/example",
"srml/executive",
"srml/metadata",
"core/sr-primitives",
"srml/session",
"srml/staking",
-1
View File
@@ -18,7 +18,6 @@ parity-codec = { version = "1.1" }
substrate-executor = { path = "../executor" }
substrate-primitives = { path = "../primitives" }
sr-io = { path = "../sr-io" }
substrate-metadata = { path = "../metadata" }
sr-primitives = { path = "../sr-primitives" }
substrate-state-machine = { path = "../state-machine" }
substrate-keyring = { path = "../../core/keyring" }
+3 -49
View File
@@ -23,7 +23,6 @@ use primitives::AuthorityId;
use runtime_primitives::{bft::Justification, generic::{BlockId, SignedBlock, Block as RuntimeBlock}};
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Zero, One, As, NumberFor};
use runtime_primitives::BuildStorage;
use substrate_metadata::JsonMetadataDecodable;
use primitives::{Blake2Hasher, RlpCodec, H256};
use primitives::storage::{StorageKey, StorageData};
use primitives::storage::well_known_keys;
@@ -254,26 +253,9 @@ impl<B, E, Block> Client<B, E, Block> where
&self.executor
}
/// Returns the runtime metadata as JSON.
pub fn json_metadata(&self, id: &BlockId<Block>) -> error::Result<String> {
self.executor.call(id, "json_metadata",&[])
.and_then(|r| Vec::<JsonMetadataDecodable>::decode(&mut &r.return_data[..])
.ok_or("JSON Metadata decoding failed".into()))
.and_then(|metadata| {
let mut json = metadata.into_iter().enumerate().fold(String::from("{"),
|mut json, (i, m)| {
if i > 0 {
json.push_str(",");
}
let (mtype, val) = m.into_json_string();
json.push_str(&format!(r#" "{}": {}"#, mtype, val));
json
}
);
json.push_str(" }");
Ok(json)
})
/// Returns the runtime metadata.
pub fn metadata(&self, id: &BlockId<Block>) -> error::Result<Vec<u8>> {
self.executor.call(id, "metadata",&[]).map(|v| v.return_data)
}
/// Reads storage value at a given block + key, returning read proof.
@@ -772,32 +754,4 @@ mod tests {
assert!(client.state_at(&BlockId::Number(1)).unwrap() != client.state_at(&BlockId::Number(0)).unwrap());
assert_eq!(client.body(&BlockId::Number(1)).unwrap().unwrap().len(), 1)
}
#[test]
fn json_metadata() {
let client = test_client::new();
let mut builder = client.new_block().unwrap();
builder.push_transfer(Transfer {
from: Keyring::Alice.to_raw_public().into(),
to: Keyring::Ferdie.to_raw_public().into(),
amount: 42,
nonce: 0,
}).unwrap();
assert!(builder.push_transfer(Transfer {
from: Keyring::Eve.to_raw_public().into(),
to: Keyring::Alice.to_raw_public().into(),
amount: 42,
nonce: 0,
}).is_err());
client.justify_and_import(BlockOrigin::Own, builder.bake().unwrap()).unwrap();
assert_eq!(
client.json_metadata(&BlockId::Number(1)).unwrap(),
r#"{ "events": { "name": "Test", "events": { "event": hallo } } }"#
);
}
}
-1
View File
@@ -21,7 +21,6 @@
extern crate substrate_bft as bft;
extern crate parity_codec as codec;
extern crate substrate_metadata;
extern crate substrate_primitives as primitives;
extern crate sr_io as runtime_io;
extern crate sr_primitives as runtime_primitives;
-13
View File
@@ -1,13 +0,0 @@
[package]
name = "substrate-metadata"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
parity-codec = { version = "1.1", default_features = false }
[features]
default = ["std"]
std = [
"parity-codec/std"
]
-190
View File
@@ -1,190 +0,0 @@
// Copyright 2017 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Decodable variant of the JsonMetadata.
//!
//! This really doesn't belong here, but is necessary for the moment. In the future
//! it should be removed entirely to an external module for shimming on to the
//! codec-encoded metadata.
#![cfg_attr(not(feature = "std"), no_std)]
extern crate parity_codec as codec;
use codec::{Encode, Output};
#[cfg(feature = "std")]
use codec::{Decode, Input};
/// The metadata of a runtime encoded as JSON.
#[derive(Eq)]
#[cfg_attr(feature = "std", derive(Debug))]
pub enum JsonMetadata {
Events { name: &'static str, events: &'static [(&'static str, fn() -> &'static str)] },
Module { module: &'static str, prefix: &'static str },
ModuleWithStorage { module: &'static str, prefix: &'static str, storage: &'static str }
}
impl Encode for JsonMetadata {
fn encode_to<W: Output>(&self, dest: &mut W) {
match self {
JsonMetadata::Events { name, events } => {
0i8.encode_to(dest);
name.encode_to(dest);
events.iter().fold(0u32, |count, _| count + 1).encode_to(dest);
events
.iter()
.map(|(module, data)| (module, data()))
.for_each(|val| val.encode_to(dest));
},
JsonMetadata::Module { module, prefix } => {
1i8.encode_to(dest);
prefix.encode_to(dest);
module.encode_to(dest);
},
JsonMetadata::ModuleWithStorage { module, prefix, storage } => {
2i8.encode_to(dest);
prefix.encode_to(dest);
module.encode_to(dest);
storage.encode_to(dest);
}
}
}
}
impl PartialEq<JsonMetadata> for JsonMetadata {
fn eq(&self, other: &JsonMetadata) -> bool {
match (self, other) {
(
JsonMetadata::Events { name: lname, events: left },
JsonMetadata::Events { name: rname, events: right }
) => {
lname == rname && left.iter().zip(right.iter()).fold(true, |res, (l, r)| {
res && l.0 == r.0 && l.1() == r.1()
})
},
(
JsonMetadata::Module { prefix: lpre, module: lmod },
JsonMetadata::Module { prefix: rpre, module: rmod }
) => {
lpre == rpre && lmod == rmod
},
(
JsonMetadata::ModuleWithStorage { prefix: lpre, module: lmod, storage: lstore },
JsonMetadata::ModuleWithStorage { prefix: rpre, module: rmod, storage: rstore }
) => {
lpre == rpre && lmod == rmod && lstore == rstore
},
_ => false,
}
}
}
/// Utility struct for making `JsonMetadata` decodeable.
#[derive(Eq, PartialEq, Debug)]
#[cfg(feature = "std")]
pub enum JsonMetadataDecodable {
Events { name: String, events: Vec<(String, String)> },
Module { module: String, prefix: String },
ModuleWithStorage { module: String, prefix: String, storage: String }
}
#[cfg(feature = "std")]
impl JsonMetadataDecodable {
/// Returns the instance as JSON string.
/// The first value of the tuple is the name of the metadata type and the second in the JSON string.
pub fn into_json_string(self) -> (&'static str, String) {
match self {
JsonMetadataDecodable::Events { name, events } => {
(
"events",
format!(
r#"{{ "name": "{}", "events": {{ {} }} }}"#, name,
events.iter().enumerate()
.fold(String::from(""), |mut json, (i, (name, data))| {
if i > 0 {
json.push_str(", ");
}
json.push_str(&format!(r#""{}": {}"#, name, data));
json
})
)
)
},
JsonMetadataDecodable::Module { prefix, module } => {
("module", format!(r#"{{ "prefix": "{}", "module": {} }}"#, prefix, module))
},
JsonMetadataDecodable::ModuleWithStorage { prefix, module, storage } => {
(
"moduleWithStorage",
format!(
r#"{{ "prefix": "{}", "module": {}, "storage": {} }}"#,
prefix, module, storage
)
)
}
}
}
}
#[cfg(feature = "std")]
impl Decode for JsonMetadataDecodable {
fn decode<I: Input>(input: &mut I) -> Option<Self> {
i8::decode(input).and_then(|variant| {
match variant {
0 => String::decode(input)
.and_then(|name| Vec::<(String, String)>::decode(input).map(|events| (name, events)))
.and_then(|(name, events)| Some(JsonMetadataDecodable::Events { name, events })),
1 => String::decode(input)
.and_then(|prefix| String::decode(input).map(|v| (prefix, v)))
.and_then(|(prefix, module)| Some(JsonMetadataDecodable::Module { prefix, module })),
2 => String::decode(input)
.and_then(|prefix| String::decode(input).map(|v| (prefix, v)))
.and_then(|(prefix, module)| String::decode(input).map(|v| (prefix, module, v)))
.and_then(|(prefix, module, storage)| Some(JsonMetadataDecodable::ModuleWithStorage { prefix, module, storage })),
_ => None,
}
})
}
}
#[cfg(feature = "std")]
impl PartialEq<JsonMetadata> for JsonMetadataDecodable {
fn eq(&self, other: &JsonMetadata) -> bool {
match (self, other) {
(
JsonMetadataDecodable::Events { name: lname, events: left },
JsonMetadata::Events { name: rname, events: right }
) => {
lname == rname && left.iter().zip(right.iter()).fold(true, |res, (l, r)| {
res && l.0 == r.0 && l.1 == r.1()
})
},
(
JsonMetadataDecodable::Module { prefix: lpre, module: lmod },
JsonMetadata::Module { prefix: rpre, module: rmod }
) => {
lpre == rpre && lmod == rmod
},
(
JsonMetadataDecodable::ModuleWithStorage { prefix: lpre, module: lmod, storage: lstore },
JsonMetadata::ModuleWithStorage { prefix: rpre, module: rmod, storage: rstore }
) => {
lpre == rpre && lmod == rmod && lstore == rstore
},
_ => false,
}
}
}
+2 -2
View File
@@ -145,8 +145,8 @@ impl<B, E, Block> StateApi<Block::Hash> for State<B, E, Block> where
fn metadata(&self, block: Trailing<Block::Hash>) -> Result<serde_json::Value> {
let block = self.unwrap_or_best(block)?;
let metadata = self.client.json_metadata(&BlockId::Hash(block))?;
serde_json::from_str(&metadata).map_err(Into::into)
let metadata = self.client.metadata(&BlockId::Hash(block))?;
serde_json::to_value(metadata).map_err(Into::into)
}
fn query_storage(&self, keys: Vec<StorageKey>, from: Block::Hash, to: Trailing<Block::Hash>) -> Result<Vec<StorageChangeSet<Block::Hash>>> {
-11
View File
@@ -133,21 +133,10 @@ pub fn run_tests(mut input: &[u8]) -> Vec<u8> {
[stxs.len() as u8].encode()
}
fn test_event_json() -> &'static str {
"hallo"
}
pub mod api {
use system;
impl_stubs!(
version => |()| super::version(),
json_metadata => |()| {
let mut vec = ::runtime_support::metadata::Vec::new();
vec.push(::runtime_support::metadata::JsonMetadata::Events {
name: "Test", events: &[ ("event", super::test_event_json) ]
});
vec
},
authorities => |()| system::authorities(),
initialise_block => |header| system::initialise_block(header),
execute_block => |block| system::execute_block(block),
+3
View File
@@ -588,6 +588,9 @@ name = "substrate-metadata"
version = "0.1.0"
dependencies = [
"parity-codec 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec-derive 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
+1 -1
View File
@@ -232,7 +232,7 @@ pub type Executive = executive::Executive<Runtime, Block, Balances, Balances, Al
pub mod api {
impl_stubs!(
version => |()| super::VERSION,
json_metadata => |()| super::Runtime::json_metadata(),
metadata => |()| super::Runtime::metadata(),
authorities => |()| super::Consensus::authorities(),
initialise_block => |header| super::Executive::initialise_block(&header),
apply_extrinsic => |extrinsic| super::Executive::apply_extrinsic(extrinsic),
+3
View File
@@ -879,6 +879,9 @@ name = "substrate-metadata"
version = "0.1.0"
dependencies = [
"parity-codec 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec-derive 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
+19
View File
@@ -0,0 +1,19 @@
[package]
name = "substrate-metadata"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
parity-codec = { version = "1.1", default_features = false }
parity-codec-derive = { version = "^1.0", default_features = false }
serde = { version = "1.0", optional = true }
serde_derive = { version = "1.0", optional = true }
[features]
default = ["std"]
std = [
"parity-codec/std",
"parity-codec-derive/std",
"serde",
"serde_derive"
]
+279
View File
@@ -0,0 +1,279 @@
// Copyright 2018 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Decodable variant of the RuntimeMetadata.
//!
//! This really doesn't belong here, but is necessary for the moment. In the future
//! it should be removed entirely to an external module for shimming on to the
//! codec-encoded metadata.
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(not(feature = "std"), feature(alloc))]
#[cfg(not(feature = "std"))]
extern crate alloc;
#[macro_use]
extern crate parity_codec_derive;
extern crate parity_codec as codec;
#[cfg(feature = "std")]
extern crate serde;
#[cfg(feature = "std")]
#[macro_use]
extern crate serde_derive;
#[cfg(feature = "std")]
pub mod alloc {
pub use std::vec;
}
use codec::{Encode, Output};
#[cfg(feature = "std")]
use codec::{Decode, Input};
// Make Vec available on `std` and `no_std`
use alloc::vec::Vec;
#[cfg(feature = "std")]
type StringBuf = String;
/// On `no_std` we do not support `Decode` and thus `StringBuf` is just `&'static str`.
/// So, if someone tries to decode this stuff on `no_std`, they will get a compilation error.
#[cfg(not(feature = "std"))]
type StringBuf = &'static str;
/// A type that decodes to a different type than it encodes.
/// The user needs to make sure that both types use the same encoding.
///
/// For example a `&'static [ &'static str ]` can be decoded to a `Vec<String>`.
#[derive(Clone)]
pub enum DecodeDifferent<B, O> where B: 'static, O: 'static {
Encode(B),
Decoded(O),
}
impl<B, O> Encode for DecodeDifferent<B, O> where B: Encode + 'static, O: Encode + 'static {
fn encode_to<W: Output>(&self, dest: &mut W) {
match self {
DecodeDifferent::Encode(b) => b.encode_to(dest),
DecodeDifferent::Decoded(o) => o.encode_to(dest),
}
}
}
#[cfg(feature = "std")]
impl<B, O> Decode for DecodeDifferent<B, O> where B: 'static, O: Decode + 'static {
fn decode<I: Input>(input: &mut I) -> Option<Self> {
<O>::decode(input).and_then(|val| {
Some(DecodeDifferent::Decoded(val))
})
}
}
impl<B, O> PartialEq for DecodeDifferent<B, O>
where
B: Encode + Eq + PartialEq + 'static,
O: Encode + Eq + PartialEq + 'static,
{
fn eq(&self, other: &Self) -> bool {
self.encode() == other.encode()
}
}
impl<B, O> Eq for DecodeDifferent<B, O>
where B: Encode + Eq + PartialEq + 'static, O: Encode + Eq + PartialEq + 'static
{}
#[cfg(feature = "std")]
impl<B, O> std::fmt::Debug for DecodeDifferent<B, O>
where
B: std::fmt::Debug + Eq + 'static,
O: std::fmt::Debug + Eq + 'static,
{
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
DecodeDifferent::Encode(b) => b.fmt(f),
DecodeDifferent::Decoded(o) => o.fmt(f),
}
}
}
#[cfg(feature = "std")]
impl<B, O> serde::Serialize for DecodeDifferent<B, O>
where
B: serde::Serialize + 'static,
O: serde::Serialize + 'static,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
DecodeDifferent::Encode(b) => b.serialize(serializer),
DecodeDifferent::Decoded(o) => o.serialize(serializer),
}
}
}
type DecodeDifferentArray<B, O=B> = DecodeDifferent<&'static [B], Vec<O>>;
#[cfg(feature = "std")]
type DecodeDifferentStr = DecodeDifferent<&'static str, StringBuf>;
#[cfg(not(feature = "std"))]
type DecodeDifferentStr = DecodeDifferent<&'static str, StringBuf>;
/// All the metadata about a module.
#[derive(Clone, PartialEq, Eq, Encode)]
#[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))]
pub struct ModuleMetadata {
pub name: DecodeDifferentStr,
pub call: CallMetadata,
}
/// All the metadata about a call.
#[derive(Clone, PartialEq, Eq, Encode)]
#[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))]
pub struct CallMetadata {
pub name: DecodeDifferentStr,
pub functions: DecodeDifferentArray<FunctionMetadata>,
}
/// All the metadata about a function.
#[derive(Clone, PartialEq, Eq, Encode)]
#[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))]
pub struct FunctionMetadata {
pub id: u16,
pub name: DecodeDifferentStr,
pub arguments: DecodeDifferentArray<FunctionArgumentMetadata>,
pub documentation: DecodeDifferentArray<&'static str, StringBuf>,
}
/// All the metadata about a function argument.
#[derive(Clone, PartialEq, Eq, Encode)]
#[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))]
pub struct FunctionArgumentMetadata {
pub name: DecodeDifferentStr,
pub ty: DecodeDifferentStr,
}
/// Newtype wrapper for support encoding functions (actual the result of the function).
#[derive(Clone, Eq)]
pub struct FnEncode<E>(pub fn() -> E) where E: Encode + 'static;
impl<E: Encode> Encode for FnEncode<E> {
fn encode_to<W: Output>(&self, dest: &mut W) {
self.0().encode_to(dest);
}
}
impl<E: Encode + PartialEq> PartialEq for FnEncode<E> {
fn eq(&self, other: &Self) -> bool {
self.0().eq(&other.0())
}
}
#[cfg(feature = "std")]
impl<E: Encode + ::std::fmt::Debug> std::fmt::Debug for FnEncode<E> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.0().fmt(f)
}
}
#[cfg(feature = "std")]
impl<E: Encode + serde::Serialize> serde::Serialize for FnEncode<E> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.0().serialize(serializer)
}
}
/// All the metadata about an outer event.
#[derive(Clone, PartialEq, Eq, Encode)]
#[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))]
pub struct OuterEventMetadata {
pub name: DecodeDifferentStr,
pub events: DecodeDifferentArray<
(&'static str, FnEncode<&'static [EventMetadata]>),
(StringBuf, Vec<EventMetadata>)
>,
}
/// All the metadata about a event.
#[derive(Clone, PartialEq, Eq, Encode)]
#[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))]
pub struct EventMetadata {
pub name: DecodeDifferentStr,
pub arguments: DecodeDifferentArray<&'static str, StringBuf>,
pub documentation: DecodeDifferentArray<&'static str, StringBuf>,
}
/// All the metadata about a storage.
#[derive(Clone, PartialEq, Eq, Encode)]
#[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))]
pub struct StorageMetadata {
pub prefix: DecodeDifferentStr,
pub functions: DecodeDifferentArray<StorageFunctionMetadata>,
}
/// All the metadata about a storage function.
#[derive(Clone, PartialEq, Eq, Encode)]
#[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))]
pub struct StorageFunctionMetadata {
pub name: DecodeDifferentStr,
pub modifier: StorageFunctionModifier,
pub ty: StorageFunctionType,
pub documentation: DecodeDifferentArray<&'static str, StringBuf>,
}
/// A storage function type.
#[derive(Clone, PartialEq, Eq, Encode)]
#[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))]
pub enum StorageFunctionType {
Plain(DecodeDifferentStr),
Map {
key: DecodeDifferentStr,
value: DecodeDifferentStr,
}
}
/// A storage function modifier.
#[derive(Clone, PartialEq, Eq, Encode)]
#[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))]
pub enum StorageFunctionModifier {
None,
Default,
Required,
}
/// All metadata about an runtime module.
#[derive(Clone, PartialEq, Eq, Encode)]
#[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))]
pub struct RuntimeModuleMetadata {
pub prefix: DecodeDifferentStr,
pub module: DecodeDifferent<FnEncode<ModuleMetadata>, ModuleMetadata>,
pub storage: Option<DecodeDifferent<FnEncode<StorageMetadata>, StorageMetadata>>,
}
/// The metadata of a runtime.
#[derive(Eq, Encode, PartialEq)]
#[cfg_attr(feature = "std", derive(Decode, Debug, Serialize))]
pub struct RuntimeMetadata {
pub outer_event: OuterEventMetadata,
pub modules: DecodeDifferentArray<RuntimeModuleMetadata>,
}
+2 -3
View File
@@ -9,15 +9,14 @@ serde = { version = "1.0", default_features = false }
serde_derive = { version = "1.0", optional = true }
parity-codec = { version = "1.1", default_features = false }
substrate-primitives = { path = "../../core/primitives", default_features = false }
substrate-metadata = { path = "../../core/metadata", default_features = false }
substrate-metadata = { path = "../metadata", default_features = false }
sr-std = { path = "../../core/sr-std", default_features = false }
sr-io = { path = "../../core/sr-io", default_features = false }
mashup = "0.1.7"
[dev-dependencies]
pretty_assertions = "0.5.1"
serde_json = { version = "1.0" }
parity-codec-derive = { version = "~1.0" }
parity-codec-derive = { version = "1.0" }
[features]
default = ["std"]
+141 -94
View File
@@ -23,6 +23,10 @@ pub use rstd::result;
#[cfg(feature = "std")]
use serde;
pub use codec::{Codec, Decode, Encode, Input, Output};
pub use substrate_metadata::{
ModuleMetadata, FunctionMetadata, DecodeDifferent,
CallMetadata, FunctionArgumentMetadata
};
pub type Result = result::Result<(), &'static str>;
@@ -306,7 +310,7 @@ macro_rules! decl_module {
d.dispatch(origin)
}
}
__dispatch_impl_json_metadata! {
__dispatch_impl_metadata! {
$mod_type $trait_instance $trait_name $call_type $origin_type
{$( $(#[doc = $doc_attr])* fn $fn_name($from $(, $param_name : $param )*) -> $result; )*}
}
@@ -461,15 +465,17 @@ macro_rules! __impl_outer_dispatch_common {
/// Implement the `json_metadata` function.
#[macro_export]
#[doc(hidden)]
macro_rules! __dispatch_impl_json_metadata {
macro_rules! __dispatch_impl_metadata {
(
$mod_type:ident $trait_instance:ident $trait_name:ident
$($rest:tt)*
) => {
impl<$trait_instance: $trait_name> $mod_type<$trait_instance> {
pub fn json_metadata() -> &'static str {
concat!(r#"{ "name": ""#, stringify!($mod_type), r#"", "call": "#,
__call_to_json!($($rest)*), " }")
pub fn metadata() -> $crate::dispatch::ModuleMetadata {
$crate::dispatch::ModuleMetadata {
name: $crate::dispatch::DecodeDifferent::Encode(stringify!($mod_type)),
call: __call_to_metadata!($($rest)*),
}
}
}
}
@@ -478,7 +484,7 @@ macro_rules! __dispatch_impl_json_metadata {
/// Convert the list of calls into their JSON representation, joined by ",".
#[macro_export]
#[doc(hidden)]
macro_rules! __call_to_json {
macro_rules! __call_to_metadata {
(
$call_type:ident $origin_type:ty
{$(
@@ -490,111 +496,111 @@ macro_rules! __call_to_json {
) -> $result:ty;
)*}
) => {
concat!(
r#"{ "name": ""#, stringify!($call_type),
r#"", "functions": {"#,
__functions_to_json!(""; 0; $origin_type; $(
$crate::dispatch::CallMetadata {
name: $crate::dispatch::DecodeDifferent::Encode(stringify!($call_type)),
functions: __functions_to_metadata!(0; $origin_type;; $(
fn $fn_name($from $(, $param_name: $param )* ) -> $result;
__function_doc_to_json!(""; $($doc_attr)*);
)*), " } }"
)
$( $doc_attr ),*;
)*),
}
};
}
/// Convert a list of functions into their JSON representation, joined by ",".
/// Convert a list of functions into a list of `FunctionMetadata` items.
#[macro_export]
#[doc(hidden)]
macro_rules! __functions_to_json {
macro_rules! __functions_to_metadata{
// ROOT
(
$prefix_str:tt;
$fn_id:expr;
$origin_type:ty;
$( $function_metadata:expr ),*;
fn $fn_name:ident(root
$(
, $param_name:ident : $param:ty
)*
) -> $result:ty;
$fn_doc:expr;
$($rest:tt)*
$( $fn_doc:expr ),*;
$( $rest:tt )*
) => {
concat!($prefix_str, " ",
__function_to_json!(
fn $fn_name(
$( $param_name : $param ),*
) -> $result;
$fn_doc;
$fn_id;
), __functions_to_json!(","; $fn_id + 1; $origin_type; $($rest)*)
)
__functions_to_metadata!(
$fn_id + 1; $origin_type;
$( $function_metadata, )* __function_to_metadata!(
fn $fn_name($( $param_name : $param ),*) -> $result; $( $fn_doc ),*; $fn_id;
);
$($rest)*
)
};
// NON ROOT
(
$prefix_str:tt;
$fn_id:expr;
$origin_type:ty;
$( $function_metadata:expr ),*;
fn $fn_name:ident(origin
$(
, $param_name:ident : $param:ty
)*
) -> $result:ty;
$fn_doc:expr;
$( $fn_doc:expr ),*;
$($rest:tt)*
) => {
concat!($prefix_str, " ",
__function_to_json!(
fn $fn_name(
origin: $origin_type
$(, $param_name : $param)*
) -> $result;
$fn_doc;
$fn_id;
), __functions_to_json!(","; $fn_id + 1; $origin_type; $($rest)*)
)
__functions_to_metadata!(
$fn_id + 1; $origin_type;
$( $function_metadata, )* __function_to_metadata!(
fn $fn_name(
origin: $origin_type
$( ,$param_name : $param )*
) -> $result; $( $fn_doc ),*; $fn_id;
);
$($rest)*
)
};
// BASE CASE
(
$prefix_str:tt;
$fn_id:expr;
$($origin_type:ty;)*
$origin_type:ty;
$( $function_metadata:expr ),*;
) => {
""
$crate::dispatch::DecodeDifferent::Encode(&[ $( $function_metadata ),* ])
}
}
/// Convert a function into its JSON representation.
/// Convert a function into its metadata representation.
#[macro_export]
#[doc(hidden)]
macro_rules! __function_to_json {
macro_rules! __function_to_metadata {
(
fn $fn_name:ident(
$first_param_name:ident : $first_param:ty $(, $param_name:ident : $param:ty)*
$($param_name:ident : $param:ty),*
) -> $result:ty;
$fn_doc:tt;
$( $fn_doc:expr ),*;
$fn_id:expr;
) => {
concat!(
r#"""#, stringify!($fn_id), r#"""#,
r#": { "name": ""#, stringify!($fn_name),
r#"", "params": [ "#,
concat!(r#"{ "name": ""#, stringify!($first_param_name), r#"", "type": ""#, stringify!($first_param), r#"" }"# ),
$crate::dispatch::FunctionMetadata {
id: $fn_id,
name: $crate::dispatch::DecodeDifferent::Encode(stringify!($fn_name)),
arguments: $crate::dispatch::DecodeDifferent::Encode(&[
$(
concat!(r#", { "name": ""#, stringify!($param_name), r#"", "type": ""#, stringify!($param), r#"" }"# ),
)*
r#" ], "description": ["#, $fn_doc, " ] }"
)
$crate::dispatch::FunctionArgumentMetadata {
name: $crate::dispatch::DecodeDifferent::Encode(stringify!($param_name)),
ty: $crate::dispatch::DecodeDifferent::Encode(stringify!($param)),
}
),*
]),
documentation: $crate::dispatch::DecodeDifferent::Encode(&[ $( $fn_doc ),* ]),
}
};
(
fn $fn_name:ident() -> $result:ty;
$fn_doc:tt;
$( $fn_doc:expr ),*;
$fn_id:expr;
) => {
concat!(
r#"""#, stringify!($fn_id), r#"""#,
r#": { "name": ""#, stringify!($fn_name),
r#"", "params": [ "#,
r#" ], "description": ["#, $fn_doc, " ] }"
)
$crate::dispatch::FunctionMetadata {
id: $fn_id,
name: $crate::dispatch::DecodeDifferent::Encode(stringify!($fn_name)),
arguments: $crate::dispatch::DecodeDifferent::Encode(&[]),
documentation: $crate::dispatch::DecodeDifferent::Encode(&[ $( $fn_doc ),* ]),
}
};
}
@@ -626,8 +632,6 @@ macro_rules! __function_doc_to_json {
#[allow(dead_code)]
mod tests {
use super::*;
use serde;
use serde_json;
pub trait Trait {
type Origin;
@@ -652,33 +656,78 @@ mod tests {
}
}
const EXPECTED_METADATA: &str = concat!(
r#"{ "name": "Module", "call": "#,
r#"{ "name": "Call", "functions": { "#,
r#""0": { "name": "aux_0", "params": [ "#,
r#"{ "name": "origin", "type": "T::Origin" }"#,
r#" ], "description": [ " Hi, this is a comment." ] }, "#,
r#""0 + 1": { "name": "aux_1", "params": [ "#,
r#"{ "name": "origin", "type": "T::Origin" }, "#,
r#"{ "name": "data", "type": "i32" }"#,
r#" ], "description": [ ] }, "#,
r#""0 + 1 + 1": { "name": "aux_2", "params": [ "#,
r#"{ "name": "origin", "type": "T::Origin" }, "#,
r#"{ "name": "data", "type": "i32" }, "#,
r#"{ "name": "data2", "type": "String" }"#,
r#" ], "description": [ ] }, "#,
r#""0 + 1 + 1 + 1": { "name": "aux_3", "params": [ "#,
r#" ], "description": [ ] }, "#,
r#""0 + 1 + 1 + 1 + 1": { "name": "aux_4", "params": [ "#,
r#"{ "name": "data", "type": "i32" }"#,
r#" ], "description": [ ] }"#,
r#" } }"#,
r#" }"#,
);
const EXPECTED_METADATA: ModuleMetadata = ModuleMetadata {
name: DecodeDifferent::Encode("Module"),
call: CallMetadata {
name: DecodeDifferent::Encode("Call"),
functions: DecodeDifferent::Encode(&[
FunctionMetadata {
id: 0,
name: DecodeDifferent::Encode("aux_0"),
arguments: DecodeDifferent::Encode(&[
FunctionArgumentMetadata {
name: DecodeDifferent::Encode("origin"),
ty: DecodeDifferent::Encode("T::Origin"),
}
]),
documentation: DecodeDifferent::Encode(&[
" Hi, this is a comment."
])
},
FunctionMetadata {
id: 1,
name: DecodeDifferent::Encode("aux_1"),
arguments: DecodeDifferent::Encode(&[
FunctionArgumentMetadata {
name: DecodeDifferent::Encode("origin"),
ty: DecodeDifferent::Encode("T::Origin"),
},
FunctionArgumentMetadata {
name: DecodeDifferent::Encode("data"),
ty: DecodeDifferent::Encode("i32"),
}
]),
documentation: DecodeDifferent::Encode(&[]),
},
FunctionMetadata {
id: 2,
name: DecodeDifferent::Encode("aux_2"),
arguments: DecodeDifferent::Encode(&[
FunctionArgumentMetadata {
name: DecodeDifferent::Encode("origin"),
ty: DecodeDifferent::Encode("T::Origin"),
},
FunctionArgumentMetadata {
name: DecodeDifferent::Encode("data"),
ty: DecodeDifferent::Encode("i32"),
},
FunctionArgumentMetadata {
name: DecodeDifferent::Encode("data2"),
ty: DecodeDifferent::Encode("String"),
}
]),
documentation: DecodeDifferent::Encode(&[]),
},
FunctionMetadata {
id: 3,
name: DecodeDifferent::Encode("aux_3"),
arguments: DecodeDifferent::Encode(&[]),
documentation: DecodeDifferent::Encode(&[]),
},
FunctionMetadata {
id: 4,
name: DecodeDifferent::Encode("aux_4"),
arguments: DecodeDifferent::Encode(&[
FunctionArgumentMetadata {
name: DecodeDifferent::Encode("data"),
ty: DecodeDifferent::Encode("i32"),
}
]),
documentation: DecodeDifferent::Encode(&[]),
}
]),
},
};
impl<T: Trait> Module<T> {
fn aux_0(_: T::Origin) -> Result {
@@ -710,9 +759,7 @@ mod tests {
#[test]
fn module_json_metadata() {
let metadata = Module::<TraitImpl>::json_metadata();
let metadata = Module::<TraitImpl>::metadata();
assert_eq!(EXPECTED_METADATA, metadata);
let _: serde::de::IgnoredAny =
serde_json::from_str(metadata).expect("Is valid json syntax");
}
}
+93 -78
View File
@@ -14,6 +14,8 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
pub use substrate_metadata::{EventMetadata, DecodeDifferent, OuterEventMetadata, FnEncode};
/// Implement the `Event` for a module.
///
/// # Simple Event Example:
@@ -125,8 +127,8 @@ macro_rules! decl_event {
}
impl Event {
#[allow(dead_code)]
pub fn event_json_metadata() -> &'static str {
concat!("{", __events_to_json!(""; $( $events )* ), " }")
pub fn metadata() -> &'static [ $crate::event::EventMetadata ] {
__events_to_metadata!(; $( $events )* )
}
}
}
@@ -226,8 +228,8 @@ macro_rules! __decl_generic_event {
}
impl<$( $generic_param ),*> RawEvent<$( $generic_param ),*> {
#[allow(dead_code)]
pub fn event_json_metadata() -> &'static str {
concat!("{", __events_to_json!(""; $( $events )* ), " }")
pub fn metadata() -> &'static [$crate::event::EventMetadata] {
__events_to_metadata!(; $( $events )* )
}
}
}
@@ -235,36 +237,31 @@ macro_rules! __decl_generic_event {
#[macro_export]
#[doc(hidden)]
macro_rules! __events_to_json {
macro_rules! __events_to_metadata {
(
$prefix_str:expr;
$( $metadata:expr ),*;
$( #[doc = $doc_attr:tt] )*
$event:ident( $first_param:path $(, $param:path )* ),
$event:ident $( ( $( $param:path ),* ) )*,
$( $rest:tt )*
) => {
concat!($prefix_str, " ", "\"", stringify!($event), r#"": { "params": [ ""#,
stringify!($first_param), "\""
$(, concat!(", \"", stringify!($param), "\"") )*, r#" ], "description": ["#,
__function_doc_to_json!(""; $( $doc_attr )*), " ] }",
__events_to_json!(","; $( $rest )*)
__events_to_metadata!(
$( $metadata, )*
$crate::event::EventMetadata {
name: $crate::event::DecodeDifferent::Encode(stringify!($event)),
arguments: $crate::event::DecodeDifferent::Encode(&[
$( $( stringify!($param) ),* )*
]),
documentation: $crate::event::DecodeDifferent::Encode(&[
$( $doc_attr ),*
]),
};
$( $rest )*
)
};
(
$prefix_str:expr;
$( #[doc = $doc_attr:tt] )*
$event:ident,
$( $rest:tt )*
$( $metadata:expr ),*;
) => {
concat!($prefix_str, " ", "\"", stringify!($event),
r#"": { "params": null, "description": ["#,
__function_doc_to_json!(""; $( $doc_attr )*), " ] }",
__events_to_json!(","; $( $rest )*)
)
};
(
$prefix_str:expr;
) => {
""
&[ $( $metadata ),* ]
}
}
@@ -408,20 +405,21 @@ macro_rules! __impl_outer_event_json_metadata {
) => {
impl $runtime {
#[allow(dead_code)]
pub fn outer_event_json_metadata() -> (&'static str, &'static [(&'static str, fn() -> &'static str)]) {
static METADATA: &[(&str, fn() -> &'static str)] = &[
("system", $system::Event::event_json_metadata)
$(
, (
stringify!($module_name),
$module_name::Event $( ::<$generic_param> )*::event_json_metadata
)
)*
];
(
stringify!($event_name),
METADATA
)
pub fn outer_event_metadata() -> $crate::event::OuterEventMetadata {
$crate::event::OuterEventMetadata {
name: $crate::event::DecodeDifferent::Encode(stringify!($event_name)),
events: $crate::event::DecodeDifferent::Encode(&[
("system", $crate::event::FnEncode(system::Event::metadata))
$(
, (
stringify!($module_name),
$crate::event::FnEncode(
$module_name::Event $( ::<$generic_param> )* ::metadata
)
)
)*
])
}
}
}
}
@@ -430,8 +428,7 @@ macro_rules! __impl_outer_event_json_metadata {
#[cfg(test)]
#[allow(dead_code)]
mod tests {
use serde;
use serde_json;
use super::*;
mod system {
pub trait Trait {
@@ -567,46 +564,64 @@ mod tests {
type Origin = u32;
}
const EXPECTED_METADATA: (&str, &[(&str, &str)]) = (
"TestEvent", &[
("system", r#"{ "SystemEvent": { "params": null, "description": [ ] } }"#),
("event_module",
concat!(
"{",
r#" "TestEvent": { "params": [ "Balance", "Origin" ], "description": [ " Hi, I am a comment." ] },"#,
r#" "EventWithoutParams": { "params": null, "description": [ " Dog" ] }"#,
" }"
)
const EXPECTED_METADATA: OuterEventMetadata = OuterEventMetadata {
name: DecodeDifferent::Encode("TestEvent"),
events: DecodeDifferent::Encode(&[
(
"system",
FnEncode(|| &[
EventMetadata {
name: DecodeDifferent::Encode("SystemEvent"),
arguments: DecodeDifferent::Encode(&[]),
documentation: DecodeDifferent::Encode(&[]),
}
])
),
("event_module2",
concat!(
"{",
r#" "TestEvent": { "params": [ "BalanceRenamed" ], "description": [ ] },"#,
r#" "TestOrigin": { "params": [ "OriginRenamed" ], "description": [ ] }"#,
" }"
)
(
"event_module",
FnEncode(|| &[
EventMetadata {
name: DecodeDifferent::Encode("TestEvent"),
arguments: DecodeDifferent::Encode(&[ "Balance", "Origin" ]),
documentation: DecodeDifferent::Encode(&[ " Hi, I am a comment." ])
},
EventMetadata {
name: DecodeDifferent::Encode("EventWithoutParams"),
arguments: DecodeDifferent::Encode(&[]),
documentation: DecodeDifferent::Encode(&[ " Dog" ]),
},
])
),
("event_module3",
concat!(
"{",
r#" "HiEvent": { "params": null, "description": [ ] }"#,
" }"
)
(
"event_module2",
FnEncode(|| &[
EventMetadata {
name: DecodeDifferent::Encode("TestEvent"),
arguments: DecodeDifferent::Encode(&[ "BalanceRenamed" ]),
documentation: DecodeDifferent::Encode(&[])
},
EventMetadata {
name: DecodeDifferent::Encode("TestOrigin"),
arguments: DecodeDifferent::Encode(&[ "OriginRenamed" ]),
documentation: DecodeDifferent::Encode(&[]),
},
])
),
]
);
(
"event_module3",
FnEncode(|| &[
EventMetadata {
name: DecodeDifferent::Encode("HiEvent"),
arguments: DecodeDifferent::Encode(&[]),
documentation: DecodeDifferent::Encode(&[])
}
])
)
])
};
#[test]
fn outer_event_json_metadata() {
let metadata = TestRuntime::outer_event_json_metadata();
assert_eq!(EXPECTED_METADATA.0, metadata.0);
assert_eq!(EXPECTED_METADATA.1.len(), metadata.1.len());
for (expected, got) in EXPECTED_METADATA.1.iter().zip(metadata.1.iter()) {
assert_eq!(expected.0, got.0);
assert_eq!(expected.1, got.1());
let _: serde::de::IgnoredAny =
serde_json::from_str(got.1()).expect(&format!("Is valid json syntax: {}", got.1()));
}
fn outer_event_metadata() {
assert_eq!(EXPECTED_METADATA, TestRuntime::outer_event_metadata());
}
}
+1 -3
View File
@@ -41,8 +41,6 @@ extern crate pretty_assertions;
#[macro_use]
extern crate serde_derive;
#[cfg(test)]
extern crate serde_json;
#[cfg(test)]
#[macro_use]
extern crate parity_codec_derive;
@@ -62,7 +60,7 @@ pub mod dispatch;
pub mod storage;
mod hashable;
#[macro_use]
mod event;
pub mod event;
#[macro_use]
pub mod metadata;
#[macro_use]
+129 -120
View File
@@ -14,38 +14,30 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use alloc;
pub use substrate_metadata::JsonMetadata;
pub use substrate_metadata::{
DecodeDifferent, FnEncode, RuntimeMetadata, RuntimeModuleMetadata
};
/// Make Box available on `std` and `no_std`.
pub type Box<T> = alloc::boxed::Box<T>;
/// Make Vec available on `std` and `no_std`.
pub type Vec<T> = alloc::vec::Vec<T>;
/// Implements the json metadata support for the given runtime and all its modules.
/// Implements the metadata support for the given runtime and all its modules.
///
/// Example:
/// ```compile_fail
/// impl_json_metadata!(for RUNTIME_NAME with modules MODULE0, MODULE2, MODULE3 with Storage);
/// impl_runtime_metadata!(for RUNTIME_NAME with modules MODULE0, MODULE2, MODULE3 with Storage);
/// ```
///
/// In this example, just `MODULE3` implements the `Storage` trait.
#[macro_export]
macro_rules! impl_json_metadata {
macro_rules! impl_runtime_metadata {
(
for $runtime:ident with modules
$( $rest:tt )*
) => {
impl $runtime {
pub fn json_metadata() -> $crate::metadata::Vec<$crate::metadata::JsonMetadata> {
let events = Self::outer_event_json_metadata();
__impl_json_metadata!($runtime;
$crate::metadata::JsonMetadata::Events {
name: events.0,
events: events.1,
};
$( $rest )*
)
pub fn metadata() -> $crate::metadata::RuntimeMetadata {
$crate::metadata::RuntimeMetadata {
outer_event: Self::outer_event_metadata(),
modules: __runtime_modules_to_metadata!($runtime;; $( $rest )*),
}
}
}
}
@@ -53,66 +45,50 @@ macro_rules! impl_json_metadata {
#[macro_export]
#[doc(hidden)]
macro_rules! __impl_json_metadata {
macro_rules! __runtime_modules_to_metadata {
(
$runtime: ident;
$( $metadata:expr ),*;
$mod:ident::$module:ident,
$( $rest:tt )*
) => {
__impl_json_metadata!(
__runtime_modules_to_metadata!(
$runtime;
$( $metadata, )* $crate::metadata::JsonMetadata::Module {
module: $mod::$module::<$runtime>::json_metadata(), prefix: stringify!($mod)
$( $metadata, )* $crate::metadata::RuntimeModuleMetadata {
prefix: $crate::metadata::DecodeDifferent::Encode(stringify!($mod)),
module: $crate::metadata::DecodeDifferent::Encode(
$crate::metadata::FnEncode($mod::$module::<$runtime>::metadata)
),
storage: None,
};
$( $rest )*
)
};
(
$runtime: ident;
$( $metadata:expr ),*;
$mod:ident::$module:ident
) => {
__impl_json_metadata!(
$runtime;
$( $metadata, )* $crate::metadata::JsonMetadata::Module {
module: $mod::$module::<$runtime>::json_metadata(), prefix: stringify!($mod)
};
)
};
(
$runtime: ident;
$( $metadata:expr ),*;
$mod:ident::$module:ident with Storage,
$( $rest:tt )*
) => {
__impl_json_metadata!(
__runtime_modules_to_metadata!(
$runtime;
$( $metadata, )* $crate::metadata::JsonMetadata::ModuleWithStorage {
module: $mod::$module::<$runtime>::json_metadata(), prefix: stringify!($mod),
storage: $mod::$module::<$runtime>::store_json_metadata()
$( $metadata, )* $crate::metadata::RuntimeModuleMetadata {
prefix: $crate::metadata::DecodeDifferent::Encode(stringify!($mod)),
module: $crate::metadata::DecodeDifferent::Encode(
$crate::metadata::FnEncode($mod::$module::<$runtime>::metadata)
),
storage: Some($crate::metadata::DecodeDifferent::Encode(
$crate::metadata::FnEncode($mod::$module::<$runtime>::store_metadata)
)),
};
$( $rest )*
)
};
(
$runtime: ident;
$( $metadata:expr ),*;
$mod:ident::$module:ident with Storage
) => {
__impl_json_metadata!(
$runtime;
$( $metadata, )* $crate::metadata::JsonMetadata::ModuleWithStorage {
module: $mod::$module::<$runtime>::json_metadata(), prefix: stringify!($mod),
storage: $mod::$module::<$runtime>::store_json_metadata()
};
)
};
(
$runtime:ident;
$( $metadata:expr ),*;
) => {
<[_]>::into_vec($crate::metadata::Box::new([ $( $metadata ),* ]))
$crate::metadata::DecodeDifferent::Encode(&[ $( $metadata ),* ])
};
}
@@ -121,9 +97,11 @@ macro_rules! __impl_json_metadata {
#[allow(dead_code)]
mod tests {
use super::*;
use serde;
use serde_json;
use substrate_metadata::JsonMetadataDecodable;
use substrate_metadata::{
EventMetadata, OuterEventMetadata, RuntimeModuleMetadata, CallMetadata, ModuleMetadata,
StorageFunctionModifier, StorageFunctionType, FunctionMetadata, FunctionArgumentMetadata,
StorageMetadata, StorageFunctionMetadata,
};
use codec::{Decode, Encode};
mod system {
@@ -219,79 +197,110 @@ mod tests {
type Origin = u32;
}
fn system_event_json() -> &'static str {
r#"{ "SystemEvent": { "params": null, "description": [ ] } }"#
}
fn event_module_event_json() -> &'static str {
r#"{ "TestEvent": { "params": [ "Balance" ], "description": [ " Hi, I am a comment." ] } }"#
}
fn event_module2_event_json() -> &'static str {
r#"{ "TestEvent": { "params": [ "Balance" ], "description": [ ] } }"#
}
impl_json_metadata!(
impl_runtime_metadata!(
for TestRuntime with modules
event_module::Module,
event_module2::ModuleWithStorage with Storage
event_module2::ModuleWithStorage with Storage,
);
const EXPECTED_METADATA: &[JsonMetadata] = &[
JsonMetadata::Events {
name: "TestEvent",
events: &[
("system", system_event_json),
("event_module", event_module_event_json),
("event_module2", event_module2_event_json),
]
const EXPECTED_METADATA: RuntimeMetadata = RuntimeMetadata {
outer_event: OuterEventMetadata {
name: DecodeDifferent::Encode("TestEvent"),
events: DecodeDifferent::Encode(&[
(
"system",
FnEncode(|| &[
EventMetadata {
name: DecodeDifferent::Encode("SystemEvent"),
arguments: DecodeDifferent::Encode(&[]),
documentation: DecodeDifferent::Encode(&[]),
}
])
),
(
"event_module",
FnEncode(|| &[
EventMetadata {
name: DecodeDifferent::Encode("TestEvent"),
arguments: DecodeDifferent::Encode(&["Balance"]),
documentation: DecodeDifferent::Encode(&[" Hi, I am a comment."])
}
])
),
(
"event_module2",
FnEncode(|| &[
EventMetadata {
name: DecodeDifferent::Encode("TestEvent"),
arguments: DecodeDifferent::Encode(&["Balance"]),
documentation: DecodeDifferent::Encode(&[])
}
])
)
]),
},
JsonMetadata::Module {
module: concat!(
r#"{ "name": "Module", "call": "#,
r#"{ "name": "Call", "functions": "#,
r#"{ "0": { "name": "aux_0", "params": [ "#,
r#"{ "name": "origin", "type": "T::Origin" } ], "#,
r#""description": [ ] } } } }"#
),
prefix: "event_module"
},
JsonMetadata::ModuleWithStorage {
module: r#"{ "name": "ModuleWithStorage", "call": { "name": "Call", "functions": { } } }"#,
prefix: "event_module2",
storage: concat!(
r#"{ "prefix": "TestStorage", "items": { "#,
r#""StorageMethod": { "description": [ ], "modifier": null, "type": "u32" }"#,
r#" } }"#
)
}
];
modules: DecodeDifferent::Encode(&[
RuntimeModuleMetadata {
prefix: DecodeDifferent::Encode("event_module"),
module: DecodeDifferent::Encode(FnEncode(||
ModuleMetadata {
name: DecodeDifferent::Encode("Module"),
call: CallMetadata {
name: DecodeDifferent::Encode("Call"),
functions: DecodeDifferent::Encode(&[
FunctionMetadata {
id: 0,
name: DecodeDifferent::Encode("aux_0"),
arguments: DecodeDifferent::Encode(&[
FunctionArgumentMetadata {
name: DecodeDifferent::Encode("origin"),
ty: DecodeDifferent::Encode("T::Origin"),
}
]),
documentation: DecodeDifferent::Encode(&[]),
}
])
}
}
)),
storage: None,
},
RuntimeModuleMetadata {
prefix: DecodeDifferent::Encode("event_module2"),
module: DecodeDifferent::Encode(FnEncode(||
ModuleMetadata {
name: DecodeDifferent::Encode("ModuleWithStorage"),
call: CallMetadata {
name: DecodeDifferent::Encode("Call"),
functions: DecodeDifferent::Encode(&[])
}
}
)),
storage: Some(DecodeDifferent::Encode(FnEncode(||
StorageMetadata {
prefix: DecodeDifferent::Encode("TestStorage"),
functions: DecodeDifferent::Encode(&[
StorageFunctionMetadata {
name: DecodeDifferent::Encode("StorageMethod"),
modifier: StorageFunctionModifier::None,
ty: StorageFunctionType::Plain(DecodeDifferent::Encode("u32")),
documentation: DecodeDifferent::Encode(&[]),
}
])
}
))),
}
])
};
#[test]
fn runtime_json_metadata() {
let metadata = TestRuntime::json_metadata();
assert_eq!(EXPECTED_METADATA, &metadata[..]);
}
fn runtime_metadata() {
let metadata = TestRuntime::metadata();
assert_eq!(EXPECTED_METADATA, metadata);
#[test]
fn json_metadata_encode_and_decode() {
let metadata = TestRuntime::json_metadata();
let metadata_encoded = metadata.encode();
let metadata_decoded = Vec::<JsonMetadataDecodable>::decode(&mut &metadata_encoded[..]);
let metadata_decoded = RuntimeMetadata::decode(&mut &metadata_encoded[..]);
assert_eq!(&metadata_decoded.unwrap()[..], &metadata[..]);
}
#[test]
fn into_json_string_is_valid_json() {
let metadata = TestRuntime::json_metadata();
let metadata_encoded = metadata.encode();
let metadata_decoded = Vec::<JsonMetadataDecodable>::decode(&mut &metadata_encoded[..]);
for mdata in metadata_decoded.unwrap().into_iter() {
let json = mdata.into_json_string();
let _: serde::de::IgnoredAny =
serde_json::from_str(&json.1).expect(&format!("Is valid json syntax: {}", json.1));
}
assert_eq!(metadata, metadata_decoded.unwrap());
}
}
+10 -10
View File
@@ -275,7 +275,7 @@ macro_rules! construct_runtime {
$name: $module::{ $( $modules $( <$modules_generic> )* ),* }
),*;
);
__decl_json_metadata!(
__decl_runtime_metadata!(
$runtime;
;
;
@@ -664,7 +664,7 @@ macro_rules! __decl_outer_dispatch {
#[macro_export]
#[doc(hidden)]
macro_rules! __decl_json_metadata {
macro_rules! __decl_runtime_metadata {
(
$runtime:ident;
;
@@ -676,7 +676,7 @@ macro_rules! __decl_json_metadata {
$( $rest_modules:ident $( <$rest_modules_generic:ident> )* ),*
})*;
) => {
__decl_json_metadata!(
__decl_runtime_metadata!(
$runtime;
$module { Module, };
$( $parsed_modules { Module $( with $parsed_storage )* } ),*;
@@ -699,7 +699,7 @@ macro_rules! __decl_json_metadata {
$( $rest_modules:ident $( <$rest_modules_generic:ident> )* ),*
})*;
) => {
__decl_json_metadata!(
__decl_runtime_metadata!(
$runtime;
;
$( $parsed_modules { Module $( with $parsed_storage )* }, )* $module { Module with Storage };
@@ -721,7 +721,7 @@ macro_rules! __decl_json_metadata {
$( $rest_modules:ident $( <$rest_modules_generic:ident> )* ),*
})*;
) => {
__decl_json_metadata!(
__decl_runtime_metadata!(
$runtime;
$module { , Storage };
$( $parsed_modules { Module $( with $parsed_storage )* } ),*;
@@ -744,7 +744,7 @@ macro_rules! __decl_json_metadata {
$( $rest_modules:ident $( <$rest_modules_generic:ident> )* ),*
})*;
) => {
__decl_json_metadata!(
__decl_runtime_metadata!(
$runtime;
;
$( $parsed_modules { Module $( with $parsed_storage )* }, )* $module { Module with Storage };
@@ -766,7 +766,7 @@ macro_rules! __decl_json_metadata {
$( $rest_modules:ident $( <$rest_modules_generic:ident> )* ),*
})*;
) => {
__decl_json_metadata!(
__decl_runtime_metadata!(
$runtime;
$( $current_module { $( $current_module_storage )* } )*;
$( $parsed_modules { Module $( with $parsed_storage )* } ),*;
@@ -787,7 +787,7 @@ macro_rules! __decl_json_metadata {
$( $rest_modules:ident $( <$rest_modules_generic:ident> )* ),*
})*;
) => {
__decl_json_metadata!(
__decl_runtime_metadata!(
$runtime;
;
$( $parsed_modules { Module $( with $parsed_storage )* }, )* $module { Module };
@@ -807,7 +807,7 @@ macro_rules! __decl_json_metadata {
$( $rest_modules:ident $( <$rest_modules_generic:ident> )* ),*
})*;
) => {
__decl_json_metadata!(
__decl_runtime_metadata!(
$runtime;
;
$( $parsed_modules { Module $( with $parsed_storage )* } ),*;
@@ -824,7 +824,7 @@ macro_rules! __decl_json_metadata {
$( $parsed_modules:ident { Module $( with $parsed_storage:ident )* } ),*;
;
) => {
impl_json_metadata!(
impl_runtime_metadata!(
for $runtime with modules
$( $parsed_modules::Module $(with $parsed_storage)*, )*
);
+448 -264
View File
@@ -53,6 +53,11 @@ pub use rstd::borrow::Borrow;
#[doc(hidden)]
pub use rstd::marker::PhantomData;
pub use substrate_metadata::{
DecodeDifferent, StorageMetadata, StorageFunctionMetadata,
StorageFunctionType, StorageFunctionModifier
};
/// Abstraction around storage.
pub trait Storage {
/// true if the key exists in storage.
@@ -530,7 +535,7 @@ macro_rules! decl_storage {
}
impl<$traitinstance: $traittype> $modulename<$traitinstance> {
__impl_store_fns!($traitinstance $($t)*);
__impl_store_json_metadata!($cratename; $($t)*);
__impl_store_metadata!($cratename; $($t)*);
}
};
(
@@ -1080,390 +1085,427 @@ macro_rules! __impl_store_item {
#[macro_export]
#[doc(hidden)]
macro_rules! __impl_store_json_metadata {
macro_rules! __impl_store_metadata {
(
$cratename:ident;
$($rest:tt)*
) => {
pub fn store_json_metadata() -> &'static str {
concat!(r#"{ "prefix": ""#, stringify!($cratename), r#"", "items": {"#,
__store_functions_to_json!(""; $($rest)*), " } }")
pub fn store_metadata() -> $crate::storage::generator::StorageMetadata {
$crate::storage::generator::StorageMetadata {
prefix: $crate::storage::generator::DecodeDifferent::Encode(stringify!($cratename)),
functions: __store_functions_to_metadata!(; $( $rest )* ),
}
}
}
}
#[macro_export]
#[doc(hidden)]
macro_rules! __store_functions_to_json {
macro_rules! __store_functions_to_metadata {
(
$prefix_str:tt;
$( $metadata:expr ),*;
$(#[doc = $doc_attr:tt])*
$name:ident :
default $ty:ty; $($t:tt)*
$name:ident : default $ty:ty;
$($t:tt)*
) => {
concat!(
__store_function_to_json!($prefix_str,
__function_doc_to_json!(""; $($doc_attr)*),
$name, __store_type_to_json!($ty), default
),
__store_functions_to_json!(","; $($t)*)
__store_functions_to_metadata!(
$( $metadata, )*
__store_function_to_metadata!(
$( $doc_attr ),*; $name, __store_type_to_metadata!($ty), default
);
$( $t )*
)
};
(
$prefix_str:tt;
$( $metadata:expr ),*;
$(#[doc = $doc_attr:tt])*
pub $name:ident :
default $ty:ty; $($t:tt)*
pub $name:ident : default $ty:ty;
$($t:tt)*
) => {
concat!(
__store_function_to_json!($prefix_str,
__function_doc_to_json!(""; $($doc_attr)*),
$name, __store_type_to_json!($ty), default
),
__store_functions_to_json!(","; $($t)*)
__store_functions_to_metadata!(
$( $metadata, )*
__store_function_to_metadata!(
$( $doc_attr ),*; $name, __store_type_to_metadata!($ty), default
);
$( $t )*
)
};
(
$prefix_str:tt;
$( $metadata:expr ),*;
$(#[doc = $doc_attr:tt])*
$name:ident :
required $ty:ty; $($t:tt)*
$name:ident : required $ty:ty;
$($t:tt)*
) => {
concat!(
__store_function_to_json!($prefix_str,
__function_doc_to_json!(""; $($doc_attr)*),
$name, __store_type_to_json!($ty), required
),
__store_functions_to_json!(","; $($t)*)
__store_functions_to_metadata!(
$( $metadata, )*
__store_function_to_metadata!(
$( $doc_attr ),*; $name, __store_type_to_metadata!($ty), required
);
$( $t )*
)
};
(
$prefix_str:tt;
$( $metadata:expr ),*;
$(#[doc = $doc_attr:tt])*
pub $name:ident :
required $ty:ty; $($t:tt)*
pub $name:ident : required $ty:ty;
$($t:tt)*
) => {
concat!(
__store_function_to_json!($prefix_str,
__function_doc_to_json!(""; $($doc_attr)*),
$name, __store_type_to_json!($ty), required
),
__store_functions_to_json!(","; $($t)*)
__store_functions_to_metadata!(
$( $metadata, )*
__store_function_to_metadata!(
$( $doc_attr ),*; $name, __store_type_to_metadata!($ty), required
);
$( $t )*
)
};
// simple values
(
$prefix_str:tt;
$( $metadata:expr ),*;
$(#[doc = $doc_attr:tt])*
$name:ident :
$ty:ty; $($t:tt)*
$name:ident : $ty:ty;
$($t:tt)*
) => {
concat!(
__store_function_to_json!($prefix_str,
__function_doc_to_json!(""; $($doc_attr)*),
$name, __store_type_to_json!($ty)
),
__store_functions_to_json!(","; $($t)*)
__store_functions_to_metadata!(
$( $metadata, )*
__store_function_to_metadata!(
$( $doc_attr ),*; $name, __store_type_to_metadata!($ty)
);
$( $t )*
)
};
(
$prefix_str:tt;
$( $metadata:expr ),*;
$(#[doc = $doc_attr:tt])*
pub $name:ident :
$ty:ty; $($t:tt)*
pub $name:ident : $ty:ty;
$($t:tt)*
) => {
concat!(
__store_function_to_json!($prefix_str,
__function_doc_to_json!(""; $($doc_attr)*),
$name, __store_type_to_json!($ty)
),
__store_functions_to_json!(","; $($t)*)
__store_functions_to_metadata!(
$( $metadata, )*
__store_function_to_metadata!(
$( $doc_attr ),*; $name, __store_type_to_metadata!($ty)
);
$( $t )*
)
};
(
$prefix_str:tt;
$( $metadata:expr ),*;
$(#[doc = $doc_attr:tt])*
$name:ident get($getfn:ident) :
default $ty:ty; $($t:tt)*
default $ty:ty;
$($t:tt)*
) => {
concat!(
__store_function_to_json!($prefix_str,
__function_doc_to_json!(""; $($doc_attr)*),
$name, __store_type_to_json!($ty), default
),
__store_functions_to_json!(","; $($t)*)
__store_functions_to_metadata!(
$( $metadata, )*
__store_function_to_metadata!(
$( $doc_attr ),*; $name, __store_type_to_metadata!($ty), default
);
$( $t )*
)
};
(
$prefix_str:tt;
$( $metadata:expr ),*;
$(#[doc = $doc_attr:tt])*
pub $name:ident get($getfn:ident) :
default $ty:ty; $($t:tt)*
default $ty:ty;
$($t:tt)*
) => {
concat!(
__store_function_to_json!($prefix_str,
__function_doc_to_json!(""; $($doc_attr)*),
$name, __store_type_to_json!($ty), default
),
__store_functions_to_json!(","; $($t)*)
__store_functions_to_metadata!(
$( $metadata, )*
__store_function_to_metadata!(
$( $doc_attr ),*; $name, __store_type_to_metadata!($ty), default
);
$( $t )*
)
};
(
$prefix_str:tt;
$( $metadata:expr ),*;
$(#[doc = $doc_attr:tt])*
$name:ident get($getfn:ident) :
required $ty:ty; $($t:tt)*
required $ty:ty;
$($t:tt)*
) => {
concat!(
__store_function_to_json!($prefix_str,
__function_doc_to_json!(""; $($doc_attr)*),
$name, __store_type_to_json!($ty), required
),
__store_functions_to_json!(","; $($t)*)
__store_functions_to_metadata!(
$( $metadata, )*
__store_function_to_metadata!(
$( $doc_attr ),*; $name, __store_type_to_metadata!($ty), required
);
$( $t )*
)
};
(
$prefix_str:tt;
$( $metadata:expr ),*;
$(#[doc = $doc_attr:tt])*
pub $name:ident get($getfn:ident) :
required $ty:ty; $($t:tt)*
required $ty:ty;
$($t:tt)*
) => {
concat!(
__store_function_to_json!($prefix_str,
__function_doc_to_json!(""; $($doc_attr)*),
$name, __store_type_to_json!($ty), required
),
__store_functions_to_json!(","; $($t)*)
__store_functions_to_metadata!(
$( $metadata, )*
__store_function_to_metadata!(
$( $doc_attr ),*; $name, __store_type_to_metadata!($ty), required
);
$( $t )*
)
};
(
$prefix_str:tt;
$( $metadata:expr ),*;
$(#[doc = $doc_attr:tt])*
$name:ident get($getfn:ident) :
$ty:ty; $($t:tt)*
$name:ident get($getfn:ident) : $ty:ty;
$( $t:tt )*
) => {
concat!(
__store_function_to_json!($prefix_str,
__function_doc_to_json!(""; $($doc_attr)*),
$name, __store_type_to_json!($ty)
),
__store_functions_to_json!(","; $($t)*)
__store_functions_to_metadata!(
$( $metadata, )*
__store_function_to_metadata!(
$( $doc_attr ),*; $name, __store_type_to_metadata!($ty)
);
$( $t )*
)
};
(
$prefix_str:tt;
$( $metadata:expr ),*;
$(#[doc = $doc_attr:tt])*
pub $name:ident get($getfn:ident) :
$ty:ty; $($t:tt)*
pub $name:ident get($getfn:ident) : $ty:ty;
$( $t:tt )*
) => {
concat!(
__store_function_to_json!($prefix_str,
__function_doc_to_json!(""; $($doc_attr)*),
$name, __store_type_to_json!($ty)
),
__store_functions_to_json!(","; $($t)*)
__store_functions_to_metadata!(
$( $metadata, )*
__store_function_to_metadata!(
$( $doc_attr ),*; $name, __store_type_to_metadata!($ty)
);
$( $t )*
)
};
// maps
(
$prefix_str:tt;
$( $metadata:expr ),*;
$(#[doc = $doc_attr:tt])*
$name:ident :
default map [$kty:ty => $ty:ty]; $($t:tt)*
default map [$kty:ty => $ty:ty];
$( $t:tt )*
) => {
concat!(
__store_function_to_json!($prefix_str,
__function_doc_to_json!(""; $($doc_attr)*),
$name, __store_type_to_json!($kty, $ty), default
),
__store_functions_to_json!(","; $($t)*)
__store_functions_to_metadata!(
$( $metadata, )*
__store_function_to_metadata!(
$( $doc_attr ),*; $name, __store_type_to_metadata!($kty, $ty), default
);
$( $t )*
)
};
(
$prefix_str:tt;
$( $metadata:expr ),*;
$(#[doc = $doc_attr:tt])*
pub $name:ident :
default map [$kty:ty => $ty:ty]; $($t:tt)*
default map [$kty:ty => $ty:ty];
$($t:tt)*
) => {
concat!(
__store_function_to_json!($prefix_str,
__function_doc_to_json!(""; $($doc_attr)*),
$name, __store_type_to_json!($kty, $ty), default
),
__store_functions_to_json!(","; $($t)*)
__store_functions_to_metadata!(
$( $metadata, )*
__store_function_to_metadata!(
$( $doc_attr ),*; $name, __store_type_to_metadata!($kty, $ty), default
);
$( $t )*
)
};
(
$prefix_str:tt;
$( $metadata:expr ),*;
$(#[doc = $doc_attr:tt])*
$name:ident :
required map [$kty:ty => $ty:ty]; $($t:tt)*
required map [$kty:ty => $ty:ty];
$($t:tt)*
) => {
concat!(
__store_function_to_json!($prefix_str,
__function_doc_to_json!(""; $($doc_attr)*),
$name, __store_type_to_json!($kty, $ty), required
),
__store_functions_to_json!(","; $($t)*)
__store_functions_to_metadata!(
$( $metadata, )*
__store_function_to_metadata!(
$( $doc_attr ),*; $name, __store_type_to_metadata!($kty, $ty), required
);
$( $t )*
)
};
(
$prefix_str:tt;
$( $metadata:expr ),*;
$(#[doc = $doc_attr:tt])*
pub $name:ident :
required map [$kty:ty => $ty:ty]; $($t:tt)*
required map [$kty:ty => $ty:ty];
$($t:tt)*
) => {
concat!(
__store_function_to_json!($prefix_str,
__function_doc_to_json!(""; $($doc_attr)*),
$name, __store_type_to_json!($kty, $ty), required
),
__store_functions_to_json!(","; $($t)*)
__store_functions_to_metadata!(
$( $metadata, )*
__store_function_to_metadata!(
$( $doc_attr ),*; $name, __store_type_to_metadata!($kty, $ty), required
);
$( $t )*
)
};
(
$prefix_str:tt;
$( $metadata:expr ),*;
$(#[doc = $doc_attr:tt])*
$name:ident :
map [$kty:ty => $ty:ty]; $($t:tt)*
map [$kty:ty => $ty:ty];
$($t:tt)*
) => {
concat!(
__store_function_to_json!($prefix_str,
__function_doc_to_json!(""; $($doc_attr)*),
$name, __store_type_to_json!($kty, $ty)
),
__store_functions_to_json!(","; $($t)*)
__store_functions_to_metadata!(
$( $metadata, )*
__store_function_to_metadata!(
$( $doc_attr ),*; $name, __store_type_to_metadata!($kty, $ty)
);
$( $t )*
)
};
(
$prefix_str:tt;
$( $metadata:expr ),*;
$(#[doc = $doc_attr:tt])*
pub $name:ident :
map [$kty:ty => $ty:ty]; $($t:tt)*
map [$kty:ty => $ty:ty];
$($t:tt)*
) => {
concat!(
__store_function_to_json!($prefix_str,
__function_doc_to_json!(""; $($doc_attr)*),
$name, __store_type_to_json!($kty, $ty)
),
__store_functions_to_json!(","; $($t)*)
__store_functions_to_metadata!(
$( $metadata, )*
__store_function_to_metadata!(
$( $doc_attr ),*; $name, __store_type_to_metadata!($kty, $ty)
);
$( $t )*
)
};
(
$prefix_str:tt;
$( $metadata:expr ),*;
$(#[doc = $doc_attr:tt])* $name:ident get($getfn:ident) :
default map [$kty:ty => $ty:ty]; $($t:tt)*
default map [$kty:ty => $ty:ty];
$($t:tt)*
) => {
concat!(
__store_function_to_json!($prefix_str,
__function_doc_to_json!(""; $($doc_attr)*),
$name, __store_type_to_json!($kty, $ty), default
),
__store_functions_to_json!(","; $($t)*)
__store_functions_to_metadata!(
$( $metadata, )*
__store_function_to_metadata!(
$( $doc_attr ),*; $name, __store_type_to_metadata!($kty, $ty), default
);
$( $t )*
)
};
(
$prefix_str:tt;
$( $metadata:expr ),*;
$(#[doc = $doc_attr:tt])*
pub $name:ident get($getfn:ident) :
default map [$kty:ty => $ty:ty]; $($t:tt)*
default map [$kty:ty => $ty:ty];
$($t:tt)*
) => {
concat!(
__store_function_to_json!($prefix_str,
__function_doc_to_json!(""; $($doc_attr)*),
$name, __store_type_to_json!($kty, $ty), default
),
__store_functions_to_json!(","; $($t)*)
__store_functions_to_metadata!(
$( $metadata, )*
__store_function_to_metadata!(
$( $doc_attr ),*; $name, __store_type_to_metadata!($kty, $ty), default
);
$( $t )*
)
};
(
$prefix_str:tt;
$( $metadata:expr ),*;
$(#[doc = $doc_attr:tt])* $name:ident get($getfn:ident) :
required map [$kty:ty => $ty:ty]; $($t:tt)*
required map [$kty:ty => $ty:ty];
$($t:tt)*
) => {
concat!(
__store_function_to_json!($prefix_str,
__function_doc_to_json!(""; $($doc_attr)*),
$name, __store_type_to_json!($kty, $ty), required
),
__store_functions_to_json!(","; $($t)*)
__store_functions_to_metadata!(
$( $metadata, )*
__store_function_to_metadata!(
$( $doc_attr ),*; $name, __store_type_to_metadata!($kty, $ty), required
);
$( $t )*
)
};
(
$prefix_str:tt;
$( $metadata:expr ),*;
$(#[doc = $doc_attr:tt])*
pub $name:ident get($getfn:ident) :
required map [$kty:ty => $ty:ty]; $($t:tt)*
required map [$kty:ty => $ty:ty];
$($t:tt)*
) => {
concat!(
__store_function_to_json!($prefix_str,
__function_doc_to_json!(""; $($doc_attr)*),
$name, __store_type_to_json!($kty, $ty), required
),
__store_functions_to_json!(","; $($t)*)
__store_functions_to_metadata!(
$( $metadata, )*
__store_function_to_metadata!(
$( $doc_attr ),*; $name, __store_type_to_metadata!($kty, $ty), required
); $( $t )*
)
};
(
$prefix_str:tt;
$( $metadata:expr ),*;
$(#[doc = $doc_attr:tt])*
$name:ident get($getfn:ident) :
map [$kty:ty => $ty:ty]; $($t:tt)*
map [$kty:ty => $ty:ty];
$($t:tt)*
) => {
concat!(
__store_function_to_json!($prefix_str,
__function_doc_to_json!(""; $($doc_attr)*),
$name, __store_type_to_json!($kty, $ty)
),
__store_functions_to_json!(","; $($t)*)
__store_functions_to_metadata!(
$( $metadata, )*
__store_function_to_metadata!(
$( $doc_attr ),*; $name, __store_type_to_metadata!($kty, $ty)
); $( $t )*
)
};
(
$prefix_str:tt;
$( $metadata:expr ),*;
$(#[doc = $doc_attr:tt])*
pub $name:ident get($getfn:ident) :
map [$kty:ty => $ty:ty]; $($t:tt)*
map [$kty:ty => $ty:ty];
$($t:tt)*
) => {
concat!(
__store_function_to_json!($prefix_str,
__function_doc_to_json!(""; $($doc_attr)*),
$name, __store_type_to_json!($kty, $ty)
),
__store_functions_to_json!(","; $($t)*)
__store_functions_to_metadata!(
$( $metadata, )*
__store_function_to_metadata!(
$( $doc_attr ),*; $name, __store_type_to_metadata!($kty, $ty)
); $( $t )*
)
};
($prefix_str:tt;) => { "" }
}
#[macro_export]
#[doc(hidden)]
macro_rules! __store_function_to_json {
($prefix_str:tt, $fn_doc:expr, $name:ident, $type:expr, $modifier:ident) => {
__store_function_to_json!($prefix_str; $fn_doc; $name; $type;
concat!("\"", stringify!($modifier), "\""))
};
($prefix_str:tt, $fn_doc:expr, $name:ident, $type:expr) => {
__store_function_to_json!($prefix_str; $fn_doc; $name; $type; "null")
};
($prefix_str:tt; $fn_doc:expr; $name:ident; $type:expr; $modifier:expr) => {
concat!($prefix_str, " \"", stringify!($name), "\": { ",
r#""description": ["#, $fn_doc, " ], ",
r#""modifier": "#, $modifier, r#", "type": "#, $type, r#" }"#
)
(
$( $metadata:expr ),*;
) => {
$crate::storage::generator::DecodeDifferent::Encode(&[
$( $metadata ),*
])
}
}
#[macro_export]
#[doc(hidden)]
macro_rules! __store_type_to_json {
macro_rules! __store_function_to_metadata {
($( $fn_doc:expr ),*; $name:ident, $type:expr, required) => {
__store_function_to_metadata!(
$( $fn_doc ),*; $name; $type;
$crate::storage::generator::StorageFunctionModifier::Required
)
};
($( $fn_doc:expr ),*; $name:ident, $type:expr, default) => {
__store_function_to_metadata!(
$( $fn_doc ),*; $name; $type;
$crate::storage::generator::StorageFunctionModifier::Default
)
};
($( $fn_doc:expr ),*; $name:ident, $type:expr) => {
__store_function_to_metadata!(
$( $fn_doc ),*; $name; $type;
$crate::storage::generator::StorageFunctionModifier::None
)
};
($( $fn_doc:expr ),*; $name:ident; $type:expr; $modifier:expr) => {
$crate::storage::generator::StorageFunctionMetadata {
name: $crate::storage::generator::DecodeDifferent::Encode(stringify!($name)),
modifier: $modifier,
ty: $type,
documentation: $crate::storage::generator::DecodeDifferent::Encode(&[ $( $fn_doc ),* ]),
}
}
}
#[macro_export]
#[doc(hidden)]
macro_rules! __store_type_to_metadata {
($name:ty) => {
concat!("\"", stringify!($name), "\"")
$crate::storage::generator::StorageFunctionType::Plain(
$crate::storage::generator::DecodeDifferent::Encode(stringify!($name)),
)
};
($key: ty, $value:ty) => {
concat!(r#"{ "key": ""#, stringify!($key), r#"", "value": ""#,
stringify!($value), "\" }")
$crate::storage::generator::StorageFunctionType::Map {
key: $crate::storage::generator::DecodeDifferent::Encode(stringify!($key)),
value: $crate::storage::generator::DecodeDifferent::Encode(stringify!($value)),
}
}
}
@@ -1475,8 +1517,6 @@ mod tests {
use std::cell::RefCell;
use codec::Codec;
use super::*;
use serde;
use serde_json;
impl Storage for RefCell<HashMap<Vec<u8>, Vec<u8>>> {
fn exists(&self, key: &[u8]) -> bool {
@@ -1580,7 +1620,6 @@ mod tests {
GETMAPU32Required get(map_get_u32_required): required map [ u32 => String ];
pub PUBMAPU32Required : required map [ u32 => String ];
pub GETPUBMAPU32Required get(map_pub_get_u32_required): required map [ u32 => String ];
}
}
@@ -1590,41 +1629,186 @@ mod tests {
type Origin = u32;
}
const EXPECTED_METADATA: &str = concat!(
r#"{ "prefix": "TestStorage", "items": { "#,
r#""U32": { "description": [ " Hello, this is doc!" ], "modifier": null, "type": "u32" }, "#,
r#""GETU32": { "description": [ ], "modifier": null, "type": "u32" }, "#,
r#""PUBU32": { "description": [ ], "modifier": null, "type": "u32" }, "#,
r#""GETPUBU32": { "description": [ ], "modifier": null, "type": "u32" }, "#,
r#""U32Default": { "description": [ ], "modifier": "default", "type": "u32" }, "#,
r#""GETU32Default": { "description": [ ], "modifier": "default", "type": "u32" }, "#,
r#""PUBU32Default": { "description": [ ], "modifier": "default", "type": "u32" }, "#,
r#""GETPUBU32Default": { "description": [ ], "modifier": "default", "type": "u32" }, "#,
r#""U32Required": { "description": [ ], "modifier": "required", "type": "u32" }, "#,
r#""GETU32Required": { "description": [ ], "modifier": "required", "type": "u32" }, "#,
r#""PUBU32Required": { "description": [ ], "modifier": "required", "type": "u32" }, "#,
r#""GETPUBU32Required": { "description": [ ], "modifier": "required", "type": "u32" }, "#,
r#""MAPU32": { "description": [ ], "modifier": null, "type": { "key": "u32", "value": "String" } }, "#,
r#""GETMAPU32": { "description": [ " Hello, this is doc!", " Hello, this is doc 2!" ], "modifier": null, "type": { "key": "u32", "value": "String" } }, "#,
r#""PUBMAPU32": { "description": [ ], "modifier": null, "type": { "key": "u32", "value": "String" } }, "#,
r#""GETPUBMAPU32": { "description": [ ], "modifier": null, "type": { "key": "u32", "value": "String" } }, "#,
r#""MAPU32Default": { "description": [ ], "modifier": "default", "type": { "key": "u32", "value": "String" } }, "#,
r#""GETMAPU32Default": { "description": [ ], "modifier": "default", "type": { "key": "u32", "value": "String" } }, "#,
r#""PUBMAPU32Default": { "description": [ ], "modifier": "default", "type": { "key": "u32", "value": "String" } }, "#,
r#""GETPUBMAPU32Default": { "description": [ ], "modifier": "default", "type": { "key": "u32", "value": "String" } }, "#,
r#""MAPU32Required": { "description": [ ], "modifier": "required", "type": { "key": "u32", "value": "String" } }, "#,
r#""GETMAPU32Required": { "description": [ ], "modifier": "required", "type": { "key": "u32", "value": "String" } }, "#,
r#""PUBMAPU32Required": { "description": [ ], "modifier": "required", "type": { "key": "u32", "value": "String" } }, "#,
r#""GETPUBMAPU32Required": { "description": [ ], "modifier": "required", "type": { "key": "u32", "value": "String" } }"#,
" } }"
);
const EXPECTED_METADATA: StorageMetadata = StorageMetadata {
prefix: DecodeDifferent::Encode("TestStorage"),
functions: DecodeDifferent::Encode(&[
StorageFunctionMetadata {
name: DecodeDifferent::Encode("U32"),
modifier: StorageFunctionModifier::None,
ty: StorageFunctionType::Plain(DecodeDifferent::Encode("u32")),
documentation: DecodeDifferent::Encode(&[ " Hello, this is doc!" ]),
},
StorageFunctionMetadata {
name: DecodeDifferent::Encode("GETU32"),
modifier: StorageFunctionModifier::None,
ty: StorageFunctionType::Plain(DecodeDifferent::Encode("u32")),
documentation: DecodeDifferent::Encode(&[]),
},
StorageFunctionMetadata {
name: DecodeDifferent::Encode("PUBU32"),
modifier: StorageFunctionModifier::None,
ty: StorageFunctionType::Plain(DecodeDifferent::Encode("u32")),
documentation: DecodeDifferent::Encode(&[]),
},
StorageFunctionMetadata {
name: DecodeDifferent::Encode("GETPUBU32"),
modifier: StorageFunctionModifier::None,
ty: StorageFunctionType::Plain(DecodeDifferent::Encode("u32")),
documentation: DecodeDifferent::Encode(&[]),
},
StorageFunctionMetadata {
name: DecodeDifferent::Encode("U32Default"),
modifier: StorageFunctionModifier::Default,
ty: StorageFunctionType::Plain(DecodeDifferent::Encode("u32")),
documentation: DecodeDifferent::Encode(&[]),
},
StorageFunctionMetadata {
name: DecodeDifferent::Encode("GETU32Default"),
modifier: StorageFunctionModifier::Default,
ty: StorageFunctionType::Plain(DecodeDifferent::Encode("u32")),
documentation: DecodeDifferent::Encode(&[]),
},
StorageFunctionMetadata {
name: DecodeDifferent::Encode("PUBU32Default"),
modifier: StorageFunctionModifier::Default,
ty: StorageFunctionType::Plain(DecodeDifferent::Encode("u32")),
documentation: DecodeDifferent::Encode(&[]),
},
StorageFunctionMetadata {
name: DecodeDifferent::Encode("GETPUBU32Default"),
modifier: StorageFunctionModifier::Default,
ty: StorageFunctionType::Plain(DecodeDifferent::Encode("u32")),
documentation: DecodeDifferent::Encode(&[]),
},
StorageFunctionMetadata {
name: DecodeDifferent::Encode("U32Required"),
modifier: StorageFunctionModifier::Required,
ty: StorageFunctionType::Plain(DecodeDifferent::Encode("u32")),
documentation: DecodeDifferent::Encode(&[]),
},
StorageFunctionMetadata {
name: DecodeDifferent::Encode("GETU32Required"),
modifier: StorageFunctionModifier::Required,
ty: StorageFunctionType::Plain(DecodeDifferent::Encode("u32")),
documentation: DecodeDifferent::Encode(&[]),
},
StorageFunctionMetadata {
name: DecodeDifferent::Encode("PUBU32Required"),
modifier: StorageFunctionModifier::Required,
ty: StorageFunctionType::Plain(DecodeDifferent::Encode("u32")),
documentation: DecodeDifferent::Encode(&[]),
},
StorageFunctionMetadata {
name: DecodeDifferent::Encode("GETPUBU32Required"),
modifier: StorageFunctionModifier::Required,
ty: StorageFunctionType::Plain(DecodeDifferent::Encode("u32")),
documentation: DecodeDifferent::Encode(&[]),
},
StorageFunctionMetadata {
name: DecodeDifferent::Encode("MAPU32"),
modifier: StorageFunctionModifier::None,
ty: StorageFunctionType::Map{
key: DecodeDifferent::Encode("u32"), value: DecodeDifferent::Encode("String")
},
documentation: DecodeDifferent::Encode(&[]),
},
StorageFunctionMetadata {
name: DecodeDifferent::Encode("GETMAPU32"),
modifier: StorageFunctionModifier::None,
ty: StorageFunctionType::Map{
key: DecodeDifferent::Encode("u32"), value: DecodeDifferent::Encode("String")
},
documentation: DecodeDifferent::Encode(&[
" Hello, this is doc!", " Hello, this is doc 2!"
]),
},
StorageFunctionMetadata {
name: DecodeDifferent::Encode("PUBMAPU32"),
modifier: StorageFunctionModifier::None,
ty: StorageFunctionType::Map{
key: DecodeDifferent::Encode("u32"), value: DecodeDifferent::Encode("String")
},
documentation: DecodeDifferent::Encode(&[]),
},
StorageFunctionMetadata {
name: DecodeDifferent::Encode("GETPUBMAPU32"),
modifier: StorageFunctionModifier::None,
ty: StorageFunctionType::Map{
key: DecodeDifferent::Encode("u32"), value: DecodeDifferent::Encode("String")
},
documentation: DecodeDifferent::Encode(&[]),
},
StorageFunctionMetadata {
name: DecodeDifferent::Encode("MAPU32Default"),
modifier: StorageFunctionModifier::Default,
ty: StorageFunctionType::Map{
key: DecodeDifferent::Encode("u32"), value: DecodeDifferent::Encode("String")
},
documentation: DecodeDifferent::Encode(&[]),
},
StorageFunctionMetadata {
name: DecodeDifferent::Encode("GETMAPU32Default"),
modifier: StorageFunctionModifier::Default,
ty: StorageFunctionType::Map{
key: DecodeDifferent::Encode("u32"), value: DecodeDifferent::Encode("String")
},
documentation: DecodeDifferent::Encode(&[]),
},
StorageFunctionMetadata {
name: DecodeDifferent::Encode("PUBMAPU32Default"),
modifier: StorageFunctionModifier::Default,
ty: StorageFunctionType::Map{
key: DecodeDifferent::Encode("u32"), value: DecodeDifferent::Encode("String")
},
documentation: DecodeDifferent::Encode(&[]),
},
StorageFunctionMetadata {
name: DecodeDifferent::Encode("GETPUBMAPU32Default"),
modifier: StorageFunctionModifier::Default,
ty: StorageFunctionType::Map{
key: DecodeDifferent::Encode("u32"), value: DecodeDifferent::Encode("String")
},
documentation: DecodeDifferent::Encode(&[]),
},
StorageFunctionMetadata {
name: DecodeDifferent::Encode("MAPU32Required"),
modifier: StorageFunctionModifier::Required,
ty: StorageFunctionType::Map{
key: DecodeDifferent::Encode("u32"), value: DecodeDifferent::Encode("String")
},
documentation: DecodeDifferent::Encode(&[]),
},
StorageFunctionMetadata {
name: DecodeDifferent::Encode("GETMAPU32Required"),
modifier: StorageFunctionModifier::Required,
ty: StorageFunctionType::Map{
key: DecodeDifferent::Encode("u32"), value: DecodeDifferent::Encode("String")
},
documentation: DecodeDifferent::Encode(&[]),
},
StorageFunctionMetadata {
name: DecodeDifferent::Encode("PUBMAPU32Required"),
modifier: StorageFunctionModifier::Required,
ty: StorageFunctionType::Map{
key: DecodeDifferent::Encode("u32"), value: DecodeDifferent::Encode("String")
},
documentation: DecodeDifferent::Encode(&[]),
},
StorageFunctionMetadata {
name: DecodeDifferent::Encode("GETPUBMAPU32Required"),
modifier: StorageFunctionModifier::Required,
ty: StorageFunctionType::Map{
key: DecodeDifferent::Encode("u32"), value: DecodeDifferent::Encode("String")
},
documentation: DecodeDifferent::Encode(&[]),
}
])
};
#[test]
fn store_json_metadata() {
let metadata = Module::<TraitImpl>::store_json_metadata();
fn store_metadata() {
let metadata = Module::<TraitImpl>::store_metadata();
assert_eq!(EXPECTED_METADATA, metadata);
let _: serde::de::IgnoredAny =
serde_json::from_str(metadata).expect("Is valid json syntax");
}
}