From 5d64be26c33ea5150a454e413d51457fefb8b2a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Thu, 20 Sep 2018 07:35:32 +0200 Subject: [PATCH] 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 --- substrate/Cargo.lock | 5 +- substrate/Cargo.toml | 2 +- substrate/core/client/Cargo.toml | 1 - substrate/core/client/src/client.rs | 52 +- substrate/core/client/src/lib.rs | 1 - substrate/core/metadata/Cargo.toml | 13 - substrate/core/metadata/src/lib.rs | 190 ----- substrate/core/rpc/src/state/mod.rs | 4 +- substrate/core/test-runtime/src/lib.rs | 11 - substrate/core/test-runtime/wasm/Cargo.lock | 3 + substrate/node/runtime/src/lib.rs | 2 +- substrate/node/runtime/wasm/Cargo.lock | 3 + substrate/srml/metadata/Cargo.toml | 19 + substrate/{core => srml}/metadata/README.adoc | 0 substrate/srml/metadata/src/lib.rs | 279 +++++++ substrate/srml/support/Cargo.toml | 5 +- substrate/srml/support/src/dispatch.rs | 235 +++--- substrate/srml/support/src/event.rs | 171 +++-- substrate/srml/support/src/lib.rs | 4 +- substrate/srml/support/src/metadata.rs | 249 +++--- substrate/srml/support/src/runtime.rs | 20 +- .../srml/support/src/storage/generator.rs | 712 +++++++++++------- 22 files changed, 1138 insertions(+), 843 deletions(-) delete mode 100644 substrate/core/metadata/Cargo.toml delete mode 100644 substrate/core/metadata/src/lib.rs create mode 100644 substrate/srml/metadata/Cargo.toml rename substrate/{core => srml}/metadata/README.adoc (100%) create mode 100644 substrate/srml/metadata/src/lib.rs diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 3c7ba0cc7a..1f7ede9b8b 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -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]] diff --git a/substrate/Cargo.toml b/substrate/Cargo.toml index d8b6e280b4..1f345b3bd8 100644 --- a/substrate/Cargo.toml +++ b/substrate/Cargo.toml @@ -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", diff --git a/substrate/core/client/Cargo.toml b/substrate/core/client/Cargo.toml index aa250e20b5..3b4258421b 100644 --- a/substrate/core/client/Cargo.toml +++ b/substrate/core/client/Cargo.toml @@ -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" } diff --git a/substrate/core/client/src/client.rs b/substrate/core/client/src/client.rs index 06d61786e0..5c27887376 100644 --- a/substrate/core/client/src/client.rs +++ b/substrate/core/client/src/client.rs @@ -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 Client where &self.executor } - /// Returns the runtime metadata as JSON. - pub fn json_metadata(&self, id: &BlockId) -> error::Result { - self.executor.call(id, "json_metadata",&[]) - .and_then(|r| Vec::::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) -> error::Result> { + 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 } } }"# - ); - } } diff --git a/substrate/core/client/src/lib.rs b/substrate/core/client/src/lib.rs index 6103953f6e..37f1f2ba6b 100644 --- a/substrate/core/client/src/lib.rs +++ b/substrate/core/client/src/lib.rs @@ -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; diff --git a/substrate/core/metadata/Cargo.toml b/substrate/core/metadata/Cargo.toml deleted file mode 100644 index 2bba98100e..0000000000 --- a/substrate/core/metadata/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "substrate-metadata" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -parity-codec = { version = "1.1", default_features = false } - -[features] -default = ["std"] -std = [ - "parity-codec/std" -] diff --git a/substrate/core/metadata/src/lib.rs b/substrate/core/metadata/src/lib.rs deleted file mode 100644 index 9267408925..0000000000 --- a/substrate/core/metadata/src/lib.rs +++ /dev/null @@ -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 . - -//! 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(&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 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(input: &mut I) -> Option { - 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 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, - } - } -} diff --git a/substrate/core/rpc/src/state/mod.rs b/substrate/core/rpc/src/state/mod.rs index e335310af7..fee8b670f3 100644 --- a/substrate/core/rpc/src/state/mod.rs +++ b/substrate/core/rpc/src/state/mod.rs @@ -145,8 +145,8 @@ impl StateApi for State where fn metadata(&self, block: Trailing) -> Result { 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, from: Block::Hash, to: Trailing) -> Result>> { diff --git a/substrate/core/test-runtime/src/lib.rs b/substrate/core/test-runtime/src/lib.rs index a907fce86b..320020b565 100644 --- a/substrate/core/test-runtime/src/lib.rs +++ b/substrate/core/test-runtime/src/lib.rs @@ -133,21 +133,10 @@ pub fn run_tests(mut input: &[u8]) -> Vec { [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), diff --git a/substrate/core/test-runtime/wasm/Cargo.lock b/substrate/core/test-runtime/wasm/Cargo.lock index e5c31a0222..d1e9174da4 100644 --- a/substrate/core/test-runtime/wasm/Cargo.lock +++ b/substrate/core/test-runtime/wasm/Cargo.lock @@ -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]] diff --git a/substrate/node/runtime/src/lib.rs b/substrate/node/runtime/src/lib.rs index 313f4093b9..9068d95d35 100644 --- a/substrate/node/runtime/src/lib.rs +++ b/substrate/node/runtime/src/lib.rs @@ -232,7 +232,7 @@ pub type Executive = executive::Executive |()| 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), diff --git a/substrate/node/runtime/wasm/Cargo.lock b/substrate/node/runtime/wasm/Cargo.lock index ecbfd049da..e238a7ff12 100644 --- a/substrate/node/runtime/wasm/Cargo.lock +++ b/substrate/node/runtime/wasm/Cargo.lock @@ -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]] diff --git a/substrate/srml/metadata/Cargo.toml b/substrate/srml/metadata/Cargo.toml new file mode 100644 index 0000000000..5194e75cbd --- /dev/null +++ b/substrate/srml/metadata/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "substrate-metadata" +version = "0.1.0" +authors = ["Parity Technologies "] + +[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" +] diff --git a/substrate/core/metadata/README.adoc b/substrate/srml/metadata/README.adoc similarity index 100% rename from substrate/core/metadata/README.adoc rename to substrate/srml/metadata/README.adoc diff --git a/substrate/srml/metadata/src/lib.rs b/substrate/srml/metadata/src/lib.rs new file mode 100644 index 0000000000..02219bcff1 --- /dev/null +++ b/substrate/srml/metadata/src/lib.rs @@ -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 . + +//! 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`. +#[derive(Clone)] +pub enum DecodeDifferent where B: 'static, O: 'static { + Encode(B), + Decoded(O), +} + +impl Encode for DecodeDifferent where B: Encode + 'static, O: Encode + 'static { + fn encode_to(&self, dest: &mut W) { + match self { + DecodeDifferent::Encode(b) => b.encode_to(dest), + DecodeDifferent::Decoded(o) => o.encode_to(dest), + } + } +} + +#[cfg(feature = "std")] +impl Decode for DecodeDifferent where B: 'static, O: Decode + 'static { + fn decode(input: &mut I) -> Option { + ::decode(input).and_then(|val| { + Some(DecodeDifferent::Decoded(val)) + }) + } +} + +impl PartialEq for DecodeDifferent +where + B: Encode + Eq + PartialEq + 'static, + O: Encode + Eq + PartialEq + 'static, +{ + fn eq(&self, other: &Self) -> bool { + self.encode() == other.encode() + } +} + +impl Eq for DecodeDifferent + where B: Encode + Eq + PartialEq + 'static, O: Encode + Eq + PartialEq + 'static +{} + +#[cfg(feature = "std")] +impl std::fmt::Debug for DecodeDifferent + 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 serde::Serialize for DecodeDifferent + where + B: serde::Serialize + 'static, + O: serde::Serialize + 'static, +{ + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + match self { + DecodeDifferent::Encode(b) => b.serialize(serializer), + DecodeDifferent::Decoded(o) => o.serialize(serializer), + } + } +} + +type DecodeDifferentArray = DecodeDifferent<&'static [B], Vec>; + +#[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, +} + +/// 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, + 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(pub fn() -> E) where E: Encode + 'static; + +impl Encode for FnEncode { + fn encode_to(&self, dest: &mut W) { + self.0().encode_to(dest); + } +} + +impl PartialEq for FnEncode { + fn eq(&self, other: &Self) -> bool { + self.0().eq(&other.0()) + } +} + +#[cfg(feature = "std")] +impl std::fmt::Debug for FnEncode { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + self.0().fmt(f) + } +} + +#[cfg(feature = "std")] +impl serde::Serialize for FnEncode { + fn serialize(&self, serializer: S) -> Result + 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) + >, +} + +/// 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, +} + +/// 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, ModuleMetadata>, + pub storage: Option, 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, +} diff --git a/substrate/srml/support/Cargo.toml b/substrate/srml/support/Cargo.toml index 785a7e68c8..40b4950a82 100644 --- a/substrate/srml/support/Cargo.toml +++ b/substrate/srml/support/Cargo.toml @@ -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"] diff --git a/substrate/srml/support/src/dispatch.rs b/substrate/srml/support/src/dispatch.rs index 47ba7785cd..d579e5cc12 100644 --- a/substrate/srml/support/src/dispatch.rs +++ b/substrate/srml/support/src/dispatch.rs @@ -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 Module { fn aux_0(_: T::Origin) -> Result { @@ -710,9 +759,7 @@ mod tests { #[test] fn module_json_metadata() { - let metadata = Module::::json_metadata(); + let metadata = Module::::metadata(); assert_eq!(EXPECTED_METADATA, metadata); - let _: serde::de::IgnoredAny = - serde_json::from_str(metadata).expect("Is valid json syntax"); } } diff --git a/substrate/srml/support/src/event.rs b/substrate/srml/support/src/event.rs index 80a3295198..8a6c153029 100644 --- a/substrate/srml/support/src/event.rs +++ b/substrate/srml/support/src/event.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . +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()); } } diff --git a/substrate/srml/support/src/lib.rs b/substrate/srml/support/src/lib.rs index 6836be27ea..873ca65fc5 100644 --- a/substrate/srml/support/src/lib.rs +++ b/substrate/srml/support/src/lib.rs @@ -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] diff --git a/substrate/srml/support/src/metadata.rs b/substrate/srml/support/src/metadata.rs index 868d400594..93e59f5343 100644 --- a/substrate/srml/support/src/metadata.rs +++ b/substrate/srml/support/src/metadata.rs @@ -14,38 +14,30 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -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 = alloc::boxed::Box; -/// Make Vec available on `std` and `no_std`. -pub type Vec = alloc::vec::Vec; - -/// 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::::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::::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()); } } diff --git a/substrate/srml/support/src/runtime.rs b/substrate/srml/support/src/runtime.rs index 8b63624a3f..a34b942664 100644 --- a/substrate/srml/support/src/runtime.rs +++ b/substrate/srml/support/src/runtime.rs @@ -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)*, )* ); diff --git a/substrate/srml/support/src/storage/generator.rs b/substrate/srml/support/src/storage/generator.rs index 1ff9731cf1..0474fdaea5 100644 --- a/substrate/srml/support/src/storage/generator.rs +++ b/substrate/srml/support/src/storage/generator.rs @@ -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, Vec>> { 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::::store_json_metadata(); + fn store_metadata() { + let metadata = Module::::store_metadata(); assert_eq!(EXPECTED_METADATA, metadata); - let _: serde::de::IgnoredAny = - serde_json::from_str(metadata).expect("Is valid json syntax"); } }