Skip to content
Snippets Groups Projects
Commit 09d5fb57 authored by Hugo Trentesaux's avatar Hugo Trentesaux
Browse files

Improve runtime calls generation (!125)

* review: disabled calls description

* remote to_vec

* fix broken markdown

* run `xtask gen-calls-doc`

* use tera templates to generate runtime calls doc
parent 585d5807
No related branches found
No related tags found
1 merge request!125Improve runtime calls generation
Pipeline #18491 passed
......@@ -8947,6 +8947,22 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "tera"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d4685e72cb35f0eb74319c8fe2d3b61e93da5609841cde2cb87fcc3bea56d20"
dependencies = [
"globwalk",
"lazy_static",
"pest",
"pest_derive",
"regex",
"serde",
"serde_json",
"unic-segment",
]
[[package]]
name = "termcolor"
version = "1.1.2"
......@@ -9374,9 +9390,9 @@ version = "1.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
dependencies = [
"cfg-if 0.1.10",
"cfg-if 1.0.0",
"digest 0.10.3",
"rand 0.7.3",
"rand 0.8.4",
"static_assertions",
]
......@@ -9415,6 +9431,56 @@ dependencies = [
"static_assertions",
]
[[package]]
name = "unic-char-property"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221"
dependencies = [
"unic-char-range",
]
[[package]]
name = "unic-char-range"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc"
[[package]]
name = "unic-common"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc"
[[package]]
name = "unic-segment"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23"
dependencies = [
"unic-ucd-segment",
]
[[package]]
name = "unic-ucd-segment"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700"
dependencies = [
"unic-char-property",
"unic-char-range",
"unic-ucd-version",
]
[[package]]
name = "unic-ucd-version"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4"
dependencies = [
"unic-common",
]
[[package]]
name = "unicase"
version = "2.6.0"
......@@ -10159,6 +10225,7 @@ dependencies = [
"scale-info",
"serde",
"serde_json",
"tera",
"tokio",
"version-compare",
"version_check",
......
This diff is collapsed.
......@@ -29,3 +29,4 @@ serde_json = "1.0"
tokio = { version = "1.15.0", features = ["macros"] }
version_check = "0.9.2"
version-compare = "0.0.11"
tera = { version = "1", default-features = false }
\ No newline at end of file
There are **{{ calls_counter }}** {{ category_name }} calls from **{{ pallets | length }}** pallets.
{% for pallet in pallets -%}
### {{ pallet.name }} - {{ pallet.index }}
{% for call in pallet.calls -%}
#### {{ call.name }} - {{ call.index }}
<details><summary><code>{{ call.name }}(
{%- for param in call.params -%}
{{ param.name }}{% if loop.last != true %}, {% endif %}
{%- endfor -%}
)</code></summary>
```rust
{% for param in call.params -%}
{{ param.name }}: {{ param.type_name }}
{% endfor -%}
```
</details>
{# replace markdown sytax in documentation breaking the final result #}
{{ call.documentation | replace(from="# WARNING:", to="WARNING:") }}
{% endfor -%}
{% endfor -%}
# Runtime calls
Calls are categorized according to the dispatch origin they require:
1. **User calls**: the dispatch origin for this kind of call must be signed by
the transactor. This is the only call category that can be submitted with an extrinsic.
1. **Root calls**: This kind of call requires a special origin that can only be invoked
through on-chain governance mechanisms.
1. **Inherent calls**: This kind of call is invoked by the author of the block itself
(usually automatically by the node).
1. **Disabled calls**: These calls can not be called directly, they are reserved for internal use by other runtime calls.
{% set pallets = user_calls_pallets -%}
{% set calls_counter = user_calls_counter -%}
{% set category_name = "user" -%}
## User calls
{% include "runtime-calls-category.md" %}
{% set pallets = root_calls_pallets -%}
{% set calls_counter = root_calls_counter -%}
{% set category_name = "root" -%}
## Root calls
{% include "runtime-calls-category.md" %}
{% set pallets = disabled_calls_pallets %}
{% set calls_counter = disabled_calls_counter %}
{% set category_name = "disabled" %}
## Disabled calls
{% include "runtime-calls-category.md" -%}
......@@ -17,15 +17,87 @@
use anyhow::{bail, Context, Result};
use codec::Decode;
use scale_info::form::PortableForm;
use serde::Serialize;
use std::{
fs::File,
io::{Read, Write},
};
use tera::Tera;
// consts
const CALLS_DOC_FILEPATH: &str = "docs/api/runtime-calls.md";
const TEMPLATES_GLOB: &str = "xtask/res/templates/*.md";
// define structs and implementations
type RuntimeCalls = Vec<Pallet>;
#[derive(Clone, Serialize)]
struct Pallet {
index: u8,
name: String,
calls: Vec<Call>,
}
impl Pallet {
fn new(
index: u8,
name: String,
scale_type_def: &scale_info::TypeDef<PortableForm>,
) -> Result<Self> {
if let scale_info::TypeDef::Variant(calls_enum) = scale_type_def {
Ok(Self {
index,
name,
calls: calls_enum.variants().iter().map(Into::into).collect(),
})
} else {
bail!("Invalid metadata")
}
}
}
#[derive(Clone, Serialize)]
struct Call {
documentation: String,
index: u8,
name: String,
params: Vec<CallParam>,
}
impl From<&scale_info::Variant<PortableForm>> for Call {
fn from(variant: &scale_info::Variant<PortableForm>) -> Self {
Self {
documentation: variant
.docs()
.iter()
.take_while(|line| !line.starts_with("# <weight>"))
.cloned()
.collect::<Vec<_>>()
.join("\n"),
index: variant.index(),
name: variant.name().to_owned(),
params: variant.fields().iter().map(Into::into).collect(),
}
}
}
#[derive(Clone, Serialize)]
struct CallParam {
name: String,
type_name: String,
}
impl From<&scale_info::Field<PortableForm>> for CallParam {
fn from(field: &scale_info::Field<PortableForm>) -> Self {
Self {
name: field.name().cloned().unwrap_or_default(),
type_name: field.type_name().cloned().unwrap_or_default(),
}
}
}
enum CallCategory {
Disabled,
Inherent,
......@@ -81,65 +153,7 @@ impl CallCategory {
}
}
#[derive(Clone)]
struct Pallet {
index: u8,
name: String,
calls: Vec<Call>,
}
impl Pallet {
fn new(
index: u8,
name: String,
scale_type_def: &scale_info::TypeDef<PortableForm>,
) -> Result<Self> {
if let scale_info::TypeDef::Variant(calls_enum) = scale_type_def {
Ok(Self {
index,
name,
calls: calls_enum.variants().iter().map(Into::into).collect(),
})
} else {
bail!("Invalid metadata")
}
}
}
#[derive(Clone)]
struct Call {
docs: Vec<String>,
index: u8,
name: String,
params: Vec<CallParam>,
}
impl From<&scale_info::Variant<PortableForm>> for Call {
fn from(variant: &scale_info::Variant<PortableForm>) -> Self {
Self {
docs: variant.docs().to_vec(),
index: variant.index(),
name: variant.name().to_owned(),
params: variant.fields().iter().map(Into::into).collect(),
}
}
}
#[derive(Clone)]
struct CallParam {
name: String,
type_name: String,
}
impl From<&scale_info::Field<PortableForm>> for CallParam {
fn from(field: &scale_info::Field<PortableForm>) -> Self {
Self {
name: field.name().cloned().unwrap_or_default(),
type_name: field.type_name().cloned().unwrap_or_default(),
}
}
}
/// generate runtime calls documentation
pub(super) fn gen_calls_doc() -> Result<()> {
// Read metadata
let mut file = std::fs::File::open("resources/metadata.scale")
......@@ -192,7 +206,9 @@ fn get_calls_from_metadata_v14(
Ok(pallets)
}
/// use template to render markdown file with runtime calls documentation
fn print_runtime_calls(pallets: RuntimeCalls) -> String {
// init variables
let mut user_calls_counter = 0;
let user_calls_pallets: RuntimeCalls = pallets
.iter()
......@@ -245,109 +261,24 @@ fn print_runtime_calls(pallets: RuntimeCalls) -> String {
})
.collect();
let mut output = String::new();
output.push_str("# Runtime calls\n\n");
output.push_str("Calls are categorized according to the dispatch origin they require:\n\n");
output.push_str(
r#"1. User calls: the dispatch origin for this kind of call must be Signed by
the transactor. This is the only call category that can be submitted with an extrinsic.
"#,
);
output.push_str(
r#"1. Root calls: This kind of call requires a special origin that can only be invoked
through on-chain governance mechanisms.
"#,
);
output.push_str(
r#"1. Inherent calls: This kind of call is invoked by the author of the block itself
(usually automatically by the node).
"#,
);
output.push_str(
r#"1. Disabled calls: These calls are disabled for different reasons (to be documented).
"#,
);
output.push_str("\n\n## User calls\n\n");
output.push_str(&print_calls_category(
user_calls_counter,
"user",
user_calls_pallets,
));
output.push_str("\n\n## Root calls\n\n");
output.push_str(&print_calls_category(
root_calls_counter,
"root",
root_calls_pallets,
));
output.push_str("\n\n## Disabled calls\n\n");
output.push_str(&print_calls_category(
disabled_calls_counter,
"disabled",
disabled_calls_pallets,
));
output
}
fn print_calls_category(calls_counter: usize, category_name: &str, pallets: Vec<Pallet>) -> String {
let mut output = String::new();
output.push_str(&format!(
"There are **{}** {} calls organized in **{}** pallets.\n",
calls_counter,
category_name,
pallets.len()
));
for pallet in pallets {
output.push_str(&format!("\n### {}: {}\n\n", pallet.index, pallet.name));
for call in pallet.calls {
output.push_str(&format!(
"<details><summary>{}: {}({})</summary>\n<p>\n\n{}</p>\n</details>\n\n",
call.index,
call.name,
print_call_params(&call.params),
print_call_details(&call),
));
// compile template
let tera = match Tera::new(TEMPLATES_GLOB) {
Ok(t) => t,
Err(e) => {
println!("Parsing error(s): {}", e);
::std::process::exit(1);
}
}
output
}
};
fn print_call_details(call: &Call) -> String {
let mut output = String::new();
output.push_str(&format!("### Index\n\n`{}`\n\n", call.index));
output.push_str(&format!(
"### Documentation\n\n{}\n\n",
call.docs
.iter()
.take_while(|line| !line.starts_with("# <weight>"))
.cloned()
.collect::<Vec<_>>()
.join("\n")
));
if !call.params.is_empty() {
output.push_str("### Types of parameters\n\n```rust\n");
output.push_str(
&call
.params
.iter()
.map(|param| format!("{}: {}", param.name, param.type_name))
.collect::<Vec<_>>()
.join(",\n"),
);
output.push_str("\n```\n\n");
}
output
}
// fills tera context for rendering
let mut context = tera::Context::new();
context.insert("user_calls_counter", &user_calls_counter);
context.insert("user_calls_pallets", &user_calls_pallets);
context.insert("root_calls_counter", &root_calls_counter);
context.insert("root_calls_pallets", &root_calls_pallets);
context.insert("disabled_calls_counter", &disabled_calls_counter);
context.insert("disabled_calls_pallets", &disabled_calls_pallets);
fn print_call_params(call_params: &[CallParam]) -> String {
call_params
.iter()
.map(|param| param.name.clone())
.collect::<Vec<_>>()
.join(", ")
tera.render("runtime-calls.md", &context)
.expect("template error")
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment