mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 21:41:12 +00:00
Refactor CLI tool to give room for growth (#667)
* refactor CLI commands for easier expansion * add license headers * cargo fmt
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use clap::Parser as ClapParser;
|
||||
use color_eyre::eyre;
|
||||
use frame_metadata::RuntimeMetadataPrefixed;
|
||||
use jsonrpsee::client_transport::ws::Uri;
|
||||
use scale::{
|
||||
Decode,
|
||||
Input,
|
||||
};
|
||||
use std::{
|
||||
fs,
|
||||
io::Read,
|
||||
path::PathBuf,
|
||||
};
|
||||
use subxt_codegen::DerivesRegistry;
|
||||
|
||||
/// Generate runtime API client code from metadata.
|
||||
///
|
||||
/// # Example (with code formatting)
|
||||
///
|
||||
/// `subxt codegen | rustfmt --edition=2018 --emit=stdout`
|
||||
#[derive(Debug, ClapParser)]
|
||||
pub struct Opts {
|
||||
/// The url of the substrate node to query for metadata for codegen.
|
||||
#[clap(name = "url", long, parse(try_from_str))]
|
||||
url: Option<Uri>,
|
||||
/// The path to the encoded metadata file.
|
||||
#[clap(short, long, parse(from_os_str))]
|
||||
file: Option<PathBuf>,
|
||||
/// Additional derives
|
||||
#[clap(long = "derive")]
|
||||
derives: Vec<String>,
|
||||
/// The `subxt` crate access path in the generated code.
|
||||
/// Defaults to `::subxt`.
|
||||
#[clap(long = "crate")]
|
||||
crate_path: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn run(opts: Opts) -> color_eyre::Result<()> {
|
||||
if let Some(file) = opts.file.as_ref() {
|
||||
if opts.url.is_some() {
|
||||
eyre::bail!("specify one of `--url` or `--file` but not both")
|
||||
};
|
||||
|
||||
let mut file = fs::File::open(file)?;
|
||||
let mut bytes = Vec::new();
|
||||
file.read_to_end(&mut bytes)?;
|
||||
codegen(&mut &bytes[..], opts.derives, opts.crate_path)?;
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
let url = opts.url.unwrap_or_else(|| {
|
||||
"http://localhost:9933"
|
||||
.parse::<Uri>()
|
||||
.expect("default url is valid")
|
||||
});
|
||||
let (_, bytes) = super::metadata::fetch_metadata(&url).await?;
|
||||
codegen(&mut &bytes[..], opts.derives, opts.crate_path)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn codegen<I: Input>(
|
||||
encoded: &mut I,
|
||||
raw_derives: Vec<String>,
|
||||
crate_path: Option<String>,
|
||||
) -> color_eyre::Result<()> {
|
||||
let metadata = <RuntimeMetadataPrefixed as Decode>::decode(encoded)?;
|
||||
let generator = subxt_codegen::RuntimeGenerator::new(metadata);
|
||||
let item_mod = syn::parse_quote!(
|
||||
pub mod api {}
|
||||
);
|
||||
|
||||
let p = raw_derives
|
||||
.iter()
|
||||
.map(|raw| syn::parse_str(raw))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let crate_path = crate_path.map(Into::into).unwrap_or_default();
|
||||
let mut derives = DerivesRegistry::new(&crate_path);
|
||||
derives.extend_for_all(p.into_iter());
|
||||
|
||||
let runtime_api = generator.generate_runtime(item_mod, derives, crate_path);
|
||||
println!("{}", runtime_api);
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use clap::Parser as ClapParser;
|
||||
use color_eyre::eyre::{
|
||||
self,
|
||||
WrapErr,
|
||||
};
|
||||
use frame_metadata::{
|
||||
RuntimeMetadata,
|
||||
RuntimeMetadataPrefixed,
|
||||
RuntimeMetadataV14,
|
||||
META_RESERVED,
|
||||
};
|
||||
use jsonrpsee::client_transport::ws::Uri;
|
||||
use scale::Decode;
|
||||
use serde::{
|
||||
Deserialize,
|
||||
Serialize,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use subxt_metadata::{
|
||||
get_metadata_hash,
|
||||
get_pallet_hash,
|
||||
};
|
||||
|
||||
/// Verify metadata compatibility between substrate nodes.
|
||||
#[derive(Debug, ClapParser)]
|
||||
pub struct Opts {
|
||||
/// Urls of the substrate nodes to verify for metadata compatibility.
|
||||
#[clap(name = "nodes", long, use_delimiter = true, parse(try_from_str))]
|
||||
nodes: Vec<Uri>,
|
||||
/// Check the compatibility of metadata for a particular pallet.
|
||||
///
|
||||
/// ### Note
|
||||
/// The validation will omit the full metadata check and focus instead on the pallet.
|
||||
#[clap(long, parse(try_from_str))]
|
||||
pallet: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn run(opts: Opts) -> color_eyre::Result<()> {
|
||||
match opts.pallet {
|
||||
Some(pallet) => {
|
||||
handle_pallet_metadata(opts.nodes.as_slice(), pallet.as_str()).await
|
||||
}
|
||||
None => handle_full_metadata(opts.nodes.as_slice()).await,
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_pallet_metadata(nodes: &[Uri], name: &str) -> color_eyre::Result<()> {
|
||||
#[derive(Serialize, Deserialize, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct CompatibilityPallet {
|
||||
pallet_present: HashMap<String, Vec<String>>,
|
||||
pallet_not_found: Vec<String>,
|
||||
}
|
||||
|
||||
let mut compatibility: CompatibilityPallet = Default::default();
|
||||
for node in nodes.iter() {
|
||||
let metadata = fetch_runtime_metadata(node).await?;
|
||||
|
||||
match metadata.pallets.iter().find(|pallet| pallet.name == name) {
|
||||
Some(pallet_metadata) => {
|
||||
let hash = get_pallet_hash(&metadata.types, pallet_metadata);
|
||||
let hex_hash = hex::encode(hash);
|
||||
println!("Node {:?} has pallet metadata hash {:?}", node, hex_hash);
|
||||
|
||||
compatibility
|
||||
.pallet_present
|
||||
.entry(hex_hash)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(node.to_string());
|
||||
}
|
||||
None => {
|
||||
compatibility.pallet_not_found.push(node.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!(
|
||||
"\nCompatible nodes by pallet\n{}",
|
||||
serde_json::to_string_pretty(&compatibility)
|
||||
.context("Failed to parse compatibility map")?
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_full_metadata(nodes: &[Uri]) -> color_eyre::Result<()> {
|
||||
let mut compatibility_map: HashMap<String, Vec<String>> = HashMap::new();
|
||||
for node in nodes.iter() {
|
||||
let metadata = fetch_runtime_metadata(node).await?;
|
||||
let hash = get_metadata_hash(&metadata);
|
||||
let hex_hash = hex::encode(hash);
|
||||
println!("Node {:?} has metadata hash {:?}", node, hex_hash,);
|
||||
|
||||
compatibility_map
|
||||
.entry(hex_hash)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(node.to_string());
|
||||
}
|
||||
|
||||
println!(
|
||||
"\nCompatible nodes\n{}",
|
||||
serde_json::to_string_pretty(&compatibility_map)
|
||||
.context("Failed to parse compatibility map")?
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn fetch_runtime_metadata(url: &Uri) -> color_eyre::Result<RuntimeMetadataV14> {
|
||||
let (_, bytes) = super::metadata::fetch_metadata(url).await?;
|
||||
|
||||
let metadata = <RuntimeMetadataPrefixed as Decode>::decode(&mut &bytes[..])?;
|
||||
if metadata.0 != META_RESERVED {
|
||||
return Err(eyre::eyre!(
|
||||
"Node {:?} has invalid metadata prefix: {:?} expected prefix: {:?}",
|
||||
url,
|
||||
metadata.0,
|
||||
META_RESERVED
|
||||
))
|
||||
}
|
||||
|
||||
match metadata.1 {
|
||||
RuntimeMetadata::V14(v14) => Ok(v14),
|
||||
_ => {
|
||||
Err(eyre::eyre!(
|
||||
"Node {:?} with unsupported metadata version: {:?}",
|
||||
url,
|
||||
metadata.1
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use clap::Parser as ClapParser;
|
||||
use color_eyre::eyre;
|
||||
use frame_metadata::RuntimeMetadataPrefixed;
|
||||
use jsonrpsee::{
|
||||
async_client::ClientBuilder,
|
||||
client_transport::ws::{
|
||||
Uri,
|
||||
WsTransportClientBuilder,
|
||||
},
|
||||
core::{
|
||||
client::ClientT,
|
||||
Error,
|
||||
},
|
||||
http_client::HttpClientBuilder,
|
||||
rpc_params,
|
||||
};
|
||||
use scale::Decode;
|
||||
use std::io::{
|
||||
self,
|
||||
Write,
|
||||
};
|
||||
|
||||
/// Download metadata from a substrate node, for use with `subxt` codegen.
|
||||
#[derive(Debug, ClapParser)]
|
||||
pub struct Opts {
|
||||
/// The url of the substrate node to query for metadata.
|
||||
#[clap(
|
||||
name = "url",
|
||||
long,
|
||||
parse(try_from_str),
|
||||
default_value = "http://localhost:9933"
|
||||
)]
|
||||
url: Uri,
|
||||
/// The format of the metadata to display: `json`, `hex` or `bytes`.
|
||||
#[clap(long, short, default_value = "bytes")]
|
||||
format: String,
|
||||
}
|
||||
|
||||
pub async fn run(opts: Opts) -> color_eyre::Result<()> {
|
||||
let (hex_data, bytes) = fetch_metadata(&opts.url).await?;
|
||||
|
||||
match opts.format.as_str() {
|
||||
"json" => {
|
||||
let metadata = <RuntimeMetadataPrefixed as Decode>::decode(&mut &bytes[..])?;
|
||||
let json = serde_json::to_string_pretty(&metadata)?;
|
||||
println!("{}", json);
|
||||
Ok(())
|
||||
}
|
||||
"hex" => {
|
||||
println!("{}", hex_data);
|
||||
Ok(())
|
||||
}
|
||||
"bytes" => Ok(io::stdout().write_all(&bytes)?),
|
||||
_ => {
|
||||
Err(eyre::eyre!(
|
||||
"Unsupported format `{}`, expected `json`, `hex` or `bytes`",
|
||||
opts.format
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn fetch_metadata(url: &Uri) -> color_eyre::Result<(String, Vec<u8>)> {
|
||||
let hex_data = match url.scheme_str() {
|
||||
Some("http") => fetch_metadata_http(url).await,
|
||||
Some("ws") | Some("wss") => fetch_metadata_ws(url).await,
|
||||
invalid_scheme => {
|
||||
let scheme = invalid_scheme.unwrap_or("no scheme");
|
||||
Err(eyre::eyre!(format!(
|
||||
"`{}` not supported, expects 'http', 'ws', or 'wss'",
|
||||
scheme
|
||||
)))
|
||||
}
|
||||
}?;
|
||||
|
||||
let bytes = hex::decode(hex_data.trim_start_matches("0x"))?;
|
||||
|
||||
Ok((hex_data, bytes))
|
||||
}
|
||||
|
||||
async fn fetch_metadata_ws(url: &Uri) -> color_eyre::Result<String> {
|
||||
let (sender, receiver) = WsTransportClientBuilder::default()
|
||||
.build(url.to_string().parse::<Uri>().unwrap())
|
||||
.await
|
||||
.map_err(|e| Error::Transport(e.into()))?;
|
||||
|
||||
let client = ClientBuilder::default()
|
||||
.max_notifs_per_subscription(4096)
|
||||
.build_with_tokio(sender, receiver);
|
||||
|
||||
Ok(client.request("state_getMetadata", rpc_params![]).await?)
|
||||
}
|
||||
|
||||
async fn fetch_metadata_http(url: &Uri) -> color_eyre::Result<String> {
|
||||
let client = HttpClientBuilder::default().build(url.to_string())?;
|
||||
|
||||
Ok(client.request::<String>("state_getMetadata", None).await?)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
pub mod codegen;
|
||||
pub mod compatibility;
|
||||
pub mod metadata;
|
||||
+12
-312
@@ -4,325 +4,25 @@
|
||||
|
||||
#![deny(unused_crate_dependencies)]
|
||||
|
||||
use color_eyre::eyre::{
|
||||
self,
|
||||
WrapErr,
|
||||
};
|
||||
use frame_metadata::{
|
||||
RuntimeMetadata,
|
||||
RuntimeMetadataPrefixed,
|
||||
RuntimeMetadataV14,
|
||||
META_RESERVED,
|
||||
};
|
||||
use jsonrpsee::{
|
||||
async_client::ClientBuilder,
|
||||
client_transport::ws::{
|
||||
Uri,
|
||||
WsTransportClientBuilder,
|
||||
},
|
||||
core::{
|
||||
client::ClientT,
|
||||
Error,
|
||||
},
|
||||
http_client::HttpClientBuilder,
|
||||
rpc_params,
|
||||
};
|
||||
use scale::{
|
||||
Decode,
|
||||
Input,
|
||||
};
|
||||
use serde::{
|
||||
Deserialize,
|
||||
Serialize,
|
||||
};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs,
|
||||
io::{
|
||||
self,
|
||||
Read,
|
||||
Write,
|
||||
},
|
||||
path::PathBuf,
|
||||
};
|
||||
use structopt::StructOpt;
|
||||
use subxt_codegen::DerivesRegistry;
|
||||
use subxt_metadata::{
|
||||
get_metadata_hash,
|
||||
get_pallet_hash,
|
||||
};
|
||||
mod commands;
|
||||
use clap::Parser as ClapParser;
|
||||
|
||||
/// Utilities for working with substrate metadata for subxt.
|
||||
#[derive(Debug, StructOpt)]
|
||||
struct Opts {
|
||||
#[structopt(subcommand)]
|
||||
command: Command,
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
/// Subxt utilities for interacting with Substrate based nodes.
|
||||
#[derive(Debug, ClapParser)]
|
||||
enum Command {
|
||||
/// Download metadata from a substrate node, for use with `subxt` codegen.
|
||||
#[structopt(name = "metadata")]
|
||||
Metadata {
|
||||
/// The url of the substrate node to query for metadata.
|
||||
#[structopt(
|
||||
name = "url",
|
||||
long,
|
||||
parse(try_from_str),
|
||||
default_value = "http://localhost:9933"
|
||||
)]
|
||||
url: Uri,
|
||||
/// The format of the metadata to display: `json`, `hex` or `bytes`.
|
||||
#[structopt(long, short, default_value = "bytes")]
|
||||
format: String,
|
||||
},
|
||||
/// Generate runtime API client code from metadata.
|
||||
///
|
||||
/// # Example (with code formatting)
|
||||
///
|
||||
/// `subxt codegen | rustfmt --edition=2018 --emit=stdout`
|
||||
Codegen {
|
||||
/// The url of the substrate node to query for metadata for codegen.
|
||||
#[structopt(name = "url", long, parse(try_from_str))]
|
||||
url: Option<Uri>,
|
||||
/// The path to the encoded metadata file.
|
||||
#[structopt(short, long, parse(from_os_str))]
|
||||
file: Option<PathBuf>,
|
||||
/// Additional derives
|
||||
#[structopt(long = "derive")]
|
||||
derives: Vec<String>,
|
||||
/// The `subxt` crate access path in the generated code.
|
||||
/// Defaults to `::subxt`.
|
||||
#[structopt(short = "crate")]
|
||||
crate_path: Option<String>,
|
||||
},
|
||||
/// Verify metadata compatibility between substrate nodes.
|
||||
Compatibility {
|
||||
/// Urls of the substrate nodes to verify for metadata compatibility.
|
||||
#[structopt(name = "nodes", long, use_delimiter = true, parse(try_from_str))]
|
||||
nodes: Vec<Uri>,
|
||||
/// Check the compatibility of metadata for a particular pallet.
|
||||
///
|
||||
/// ### Note
|
||||
/// The validation will omit the full metadata check and focus instead on the pallet.
|
||||
#[structopt(long, parse(try_from_str))]
|
||||
pallet: Option<String>,
|
||||
},
|
||||
Metadata(commands::metadata::Opts),
|
||||
Codegen(commands::codegen::Opts),
|
||||
Compatibility(commands::compatibility::Opts),
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> color_eyre::Result<()> {
|
||||
color_eyre::install()?;
|
||||
let args = Opts::from_args();
|
||||
let args = Command::parse();
|
||||
|
||||
match args.command {
|
||||
Command::Metadata { url, format } => {
|
||||
let (hex_data, bytes) = fetch_metadata(&url).await?;
|
||||
|
||||
match format.as_str() {
|
||||
"json" => {
|
||||
let metadata =
|
||||
<RuntimeMetadataPrefixed as Decode>::decode(&mut &bytes[..])?;
|
||||
let json = serde_json::to_string_pretty(&metadata)?;
|
||||
println!("{}", json);
|
||||
Ok(())
|
||||
}
|
||||
"hex" => {
|
||||
println!("{}", hex_data);
|
||||
Ok(())
|
||||
}
|
||||
"bytes" => Ok(io::stdout().write_all(&bytes)?),
|
||||
_ => {
|
||||
Err(eyre::eyre!(
|
||||
"Unsupported format `{}`, expected `json`, `hex` or `bytes`",
|
||||
format
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
Command::Codegen {
|
||||
url,
|
||||
file,
|
||||
derives,
|
||||
crate_path,
|
||||
} => {
|
||||
if let Some(file) = file.as_ref() {
|
||||
if url.is_some() {
|
||||
eyre::bail!("specify one of `--url` or `--file` but not both")
|
||||
};
|
||||
|
||||
let mut file = fs::File::open(file)?;
|
||||
let mut bytes = Vec::new();
|
||||
file.read_to_end(&mut bytes)?;
|
||||
codegen(&mut &bytes[..], derives, crate_path)?;
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
let url = url.unwrap_or_else(|| {
|
||||
"http://localhost:9933"
|
||||
.parse::<Uri>()
|
||||
.expect("default url is valid")
|
||||
});
|
||||
let (_, bytes) = fetch_metadata(&url).await?;
|
||||
codegen(&mut &bytes[..], derives, crate_path)?;
|
||||
Ok(())
|
||||
}
|
||||
Command::Compatibility { nodes, pallet } => {
|
||||
match pallet {
|
||||
Some(pallet) => {
|
||||
handle_pallet_metadata(nodes.as_slice(), pallet.as_str()).await
|
||||
}
|
||||
None => handle_full_metadata(nodes.as_slice()).await,
|
||||
}
|
||||
}
|
||||
match args {
|
||||
Command::Metadata(opts) => commands::metadata::run(opts).await,
|
||||
Command::Codegen(opts) => commands::codegen::run(opts).await,
|
||||
Command::Compatibility(opts) => commands::compatibility::run(opts).await,
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_pallet_metadata(nodes: &[Uri], name: &str) -> color_eyre::Result<()> {
|
||||
#[derive(Serialize, Deserialize, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct CompatibilityPallet {
|
||||
pallet_present: HashMap<String, Vec<String>>,
|
||||
pallet_not_found: Vec<String>,
|
||||
}
|
||||
|
||||
let mut compatibility: CompatibilityPallet = Default::default();
|
||||
for node in nodes.iter() {
|
||||
let metadata = fetch_runtime_metadata(node).await?;
|
||||
|
||||
match metadata.pallets.iter().find(|pallet| pallet.name == name) {
|
||||
Some(pallet_metadata) => {
|
||||
let hash = get_pallet_hash(&metadata.types, pallet_metadata);
|
||||
let hex_hash = hex::encode(hash);
|
||||
println!("Node {:?} has pallet metadata hash {:?}", node, hex_hash);
|
||||
|
||||
compatibility
|
||||
.pallet_present
|
||||
.entry(hex_hash)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(node.to_string());
|
||||
}
|
||||
None => {
|
||||
compatibility.pallet_not_found.push(node.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!(
|
||||
"\nCompatible nodes by pallet\n{}",
|
||||
serde_json::to_string_pretty(&compatibility)
|
||||
.context("Failed to parse compatibility map")?
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_full_metadata(nodes: &[Uri]) -> color_eyre::Result<()> {
|
||||
let mut compatibility_map: HashMap<String, Vec<String>> = HashMap::new();
|
||||
for node in nodes.iter() {
|
||||
let metadata = fetch_runtime_metadata(node).await?;
|
||||
let hash = get_metadata_hash(&metadata);
|
||||
let hex_hash = hex::encode(hash);
|
||||
println!("Node {:?} has metadata hash {:?}", node, hex_hash,);
|
||||
|
||||
compatibility_map
|
||||
.entry(hex_hash)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(node.to_string());
|
||||
}
|
||||
|
||||
println!(
|
||||
"\nCompatible nodes\n{}",
|
||||
serde_json::to_string_pretty(&compatibility_map)
|
||||
.context("Failed to parse compatibility map")?
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn fetch_runtime_metadata(url: &Uri) -> color_eyre::Result<RuntimeMetadataV14> {
|
||||
let (_, bytes) = fetch_metadata(url).await?;
|
||||
|
||||
let metadata = <RuntimeMetadataPrefixed as Decode>::decode(&mut &bytes[..])?;
|
||||
if metadata.0 != META_RESERVED {
|
||||
return Err(eyre::eyre!(
|
||||
"Node {:?} has invalid metadata prefix: {:?} expected prefix: {:?}",
|
||||
url,
|
||||
metadata.0,
|
||||
META_RESERVED
|
||||
))
|
||||
}
|
||||
|
||||
match metadata.1 {
|
||||
RuntimeMetadata::V14(v14) => Ok(v14),
|
||||
_ => {
|
||||
Err(eyre::eyre!(
|
||||
"Node {:?} with unsupported metadata version: {:?}",
|
||||
url,
|
||||
metadata.1
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn fetch_metadata_ws(url: &Uri) -> color_eyre::Result<String> {
|
||||
let (sender, receiver) = WsTransportClientBuilder::default()
|
||||
.build(url.to_string().parse::<Uri>().unwrap())
|
||||
.await
|
||||
.map_err(|e| Error::Transport(e.into()))?;
|
||||
|
||||
let client = ClientBuilder::default()
|
||||
.max_notifs_per_subscription(4096)
|
||||
.build_with_tokio(sender, receiver);
|
||||
|
||||
Ok(client.request("state_getMetadata", rpc_params![]).await?)
|
||||
}
|
||||
|
||||
async fn fetch_metadata_http(url: &Uri) -> color_eyre::Result<String> {
|
||||
let client = HttpClientBuilder::default().build(url.to_string())?;
|
||||
|
||||
Ok(client.request::<String>("state_getMetadata", None).await?)
|
||||
}
|
||||
|
||||
async fn fetch_metadata(url: &Uri) -> color_eyre::Result<(String, Vec<u8>)> {
|
||||
let hex_data = match url.scheme_str() {
|
||||
Some("http") => fetch_metadata_http(url).await,
|
||||
Some("ws") | Some("wss") => fetch_metadata_ws(url).await,
|
||||
invalid_scheme => {
|
||||
let scheme = invalid_scheme.unwrap_or("no scheme");
|
||||
Err(eyre::eyre!(format!(
|
||||
"`{}` not supported, expects 'http', 'ws', or 'wss'",
|
||||
scheme
|
||||
)))
|
||||
}
|
||||
}?;
|
||||
|
||||
let bytes = hex::decode(hex_data.trim_start_matches("0x"))?;
|
||||
|
||||
Ok((hex_data, bytes))
|
||||
}
|
||||
|
||||
fn codegen<I: Input>(
|
||||
encoded: &mut I,
|
||||
raw_derives: Vec<String>,
|
||||
crate_path: Option<String>,
|
||||
) -> color_eyre::Result<()> {
|
||||
let metadata = <RuntimeMetadataPrefixed as Decode>::decode(encoded)?;
|
||||
let generator = subxt_codegen::RuntimeGenerator::new(metadata);
|
||||
let item_mod = syn::parse_quote!(
|
||||
pub mod api {}
|
||||
);
|
||||
|
||||
let p = raw_derives
|
||||
.iter()
|
||||
.map(|raw| syn::parse_str(raw))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let crate_path = crate_path.map(Into::into).unwrap_or_default();
|
||||
let mut derives = DerivesRegistry::new(&crate_path);
|
||||
derives.extend_for_all(p.into_iter());
|
||||
|
||||
let runtime_api = generator.generate_runtime(item_mod, derives, crate_path);
|
||||
println!("{}", runtime_api);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user