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
+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");
}
}