Skip to content

Commit

Permalink
feat: add option to output metadata to a file (#61)
Browse files Browse the repository at this point in the history
* add option to output metadata to a file in either scale or json encoding

* refactor metadata printing

* cargo clippy --fix and cargo fmt

* don't panic on broken pipe

* update cargo lock

* improve error handling by properly installing eyre and replacing lots of panics and unwraps

* Minor fixes

* Add doc and user goodie

* cargo +nightly clippy --fix

* fix typo

* Generate doc

---------

Co-authored-by: Chevdor <chevdor@users.noreply.github.com>
Co-authored-by: Chevdor <chevdor@gmail.com>
  • Loading branch information
3 people committed Feb 7, 2023
1 parent b0b8303 commit cfed868
Show file tree
Hide file tree
Showing 13 changed files with 375 additions and 342 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ data
.env
scripts/out
*.gz
metadata.*
21 changes: 1 addition & 20 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

259 changes: 96 additions & 163 deletions README.md

Large diffs are not rendered by default.

50 changes: 42 additions & 8 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod opts;

use std::io::Write;

use clap::{crate_name, crate_version, Parser};
// use color_eyre::owo_colors::OwoColorize;
use env_logger::Env;
Expand All @@ -20,6 +22,8 @@ macro_rules! noquiet {
fn main() -> color_eyre::Result<()> {
env_logger::Builder::from_env(Env::default().default_filter_or("none")).init();
let opts: Opts = Opts::parse();
color_eyre::install()?;

noquiet!(opts, println!("Running {} v{}", crate_name!(), crate_version!()));

match opts.subcmd {
Expand Down Expand Up @@ -57,13 +61,43 @@ fn main() -> color_eyre::Result<()> {
info!("⏱️ Loading WASM from {:?}", &source);
let subwasm = Subwasm::new(&source);

if let Some(filter) = meta_opts.module {
subwasm.display_module(filter);
} else if opts.json {
subwasm.display_metadata_json()
} else {
subwasm.display_modules_list()
let mut fmt: OutputFormat = meta_opts.format.unwrap_or_else(|| "human".into()).into();
if opts.json {
eprintln!("--json is DEPRECATED, use --format=json instead");
fmt = OutputFormat::Json;
}

let mut output = meta_opts.output;
if let Some(out) = &output {
if out.is_empty() || out == "auto" {
match fmt {
OutputFormat::Human => output = Some("metadata.txt".into()),
OutputFormat::Json => output = Some("metadata.json".into()),
OutputFormat::Scale => output = Some("metadata.scale".into()),
OutputFormat::HexScale => output = Some("metadata.hex".into()),
OutputFormat::JsonScale => output = Some("metadata.jscale".into()),
}
}
}

let mut out: Box<dyn Write> = if let Some(output) = &output {
Box::new(std::fs::File::create(output)?)
} else {
Box::new(std::io::stdout())
};

match subwasm.write_metadata(fmt, meta_opts.module, &mut out) {
Ok(_) => Ok(()),
Err(e) => {
if let Some(e) = e.root_cause().downcast_ref::<std::io::Error>() {
if e.kind() == std::io::ErrorKind::BrokenPipe {
log::debug!("ignoring broken pipe error: {:?}", e);
return Ok(());
}
}
Err(e)
}
}?
}

SubCommand::Diff(diff_opts) => {
Expand All @@ -77,11 +111,11 @@ fn main() -> color_eyre::Result<()> {
}

SubCommand::Compress(copts) => {
compress(copts.input, copts.output).unwrap();
compress(copts.input, copts.output)?;
}

SubCommand::Decompress(dopts) => {
decompress(dopts.input, dopts.output).unwrap();
decompress(dopts.input, dopts.output)?;
}
};

Expand Down
9 changes: 9 additions & 0 deletions cli/src/opts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,15 @@ pub struct MetaOpts {
/// Currently, you must pass a block hash. Passing the block numbers is not supported.
#[clap(short, long)]
pub block: Option<String>, // TODO: can do better...

/// You may specifiy the output format. One of "human", "scale", "json", "json+scale", "hex+scale"
#[clap(long, short, default_value = "human")]
pub format: Option<String>,

/// You may specifiy the output filename where the metadata will be saved.
/// Alternatively, you may use `auto` and an appropriate name will be generated according to the `format` your chose.
#[clap(short, long)]
pub output: Option<String>,
}

/// Compare 2 runtimes
Expand Down
20 changes: 10 additions & 10 deletions cli/tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ mod cli_tests {
fn it_gets_a_runtime() {
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap();

let assert = cmd.args(&["get", "--output", "runtime.wasm", "wss://rpc.polkadot.io:443"]).assert();
let assert = cmd.args(["get", "--output", "runtime.wasm", "wss://rpc.polkadot.io:443"]).assert();
assert.success().code(0);
assert!(Path::new("runtime.wasm").exists());
}
Expand All @@ -41,7 +41,7 @@ mod cli_tests {
fn it_fails_on_bad_chain() {
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap();

let assert = cmd.args(&["get", "--chain", "foobar"]).assert();
let assert = cmd.args(["get", "--chain", "foobar"]).assert();
assert.failure().code(101);
}
}
Expand All @@ -53,11 +53,11 @@ mod cli_tests {
#[test]
fn it_shows_metadata() {
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap();
let assert = cmd.args(&["get", "wss://rpc.polkadot.io:443", "--output", "runtime.wasm"]).assert();
let assert = cmd.args(["get", "wss://rpc.polkadot.io:443", "--output", "runtime.wasm"]).assert();
assert.success().code(0);

let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap();
let assert = cmd.args(&["meta", "runtime.wasm"]).assert();
let assert = cmd.args(["meta", "runtime.wasm"]).assert();
assert.success().code(0);
}
}
Expand All @@ -69,27 +69,27 @@ mod cli_tests {
#[test]
fn it_does_basic_compress_decompress() {
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap();
let assert = cmd.args(&["get", "wss://rpc.polkadot.io:443", "--output", "compressed.wasm"]).assert();
let assert = cmd.args(["get", "wss://rpc.polkadot.io:443", "--output", "compressed.wasm"]).assert();
assert.success().code(0);

let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap();
cmd.args(&["decompress", "compressed.wasm", "decompressed.wasm"]).assert().success().code(0);
cmd.args(["decompress", "compressed.wasm", "decompressed.wasm"]).assert().success().code(0);

let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap();
cmd.args(&["compress", "decompressed.wasm", "new_compressed.wasm"]).assert().success().code(0);
cmd.args(["compress", "decompressed.wasm", "new_compressed.wasm"]).assert().success().code(0);
}

#[test]
fn it_does_decompress_on_already() {
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap();
let assert = cmd.args(&["get", "wss://rpc.polkadot.io:443", "--output", "compressed.wasm"]).assert();
let assert = cmd.args(["get", "wss://rpc.polkadot.io:443", "--output", "compressed.wasm"]).assert();
assert.success().code(0);

let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap();
cmd.args(&["decompress", "compressed.wasm", "decompressed.wasm"]).assert().success().code(0);
cmd.args(["decompress", "compressed.wasm", "decompressed.wasm"]).assert().success().code(0);

let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap();
cmd.args(&["decompress", "decompressed.wasm", "new_decompressed.wasm"]).assert().success().code(0);
cmd.args(["decompress", "decompressed.wasm", "new_decompressed.wasm"]).assert().success().code(0);
}
}
}
2 changes: 2 additions & 0 deletions doc/usage_meta.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,7 @@ Options:
-j, --json Output as json
-m, --module <MODULE> Without this flag, the metadata command display the list of all modules. Using this flag, you will only see the module of your choice and a few details about it
-b, --block <BLOCK> The optional block where to fetch the runtime. That allows fetching older runtimes but you will need to connect to archive nodes. Currently, you must pass a block hash. Passing the block numbers is not supported
-f, --format <FORMAT> You may specifiy the output format. One of "human", "scale", "json", "json+scale", "hex+scale" [default: human]
-o, --output <OUTPUT> You may specifiy the output filename where the metadata will be saved. Alternatively, you may use `auto` and an appropriate name will be generated according to the `format` your chose
-h, --help Print help information
-V, --version Print version information
2 changes: 1 addition & 1 deletion lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ repository = "https://github.com/chevdor/subwasm"
version = "0.18.0"

[dependencies]
calm_io = "0.1"
color-eyre = "0.6"
frame-metadata = { version = "15", package = "frame-metadata", features = [
"v12",
Expand All @@ -39,3 +38,4 @@ substrate-differ = { version = "0.18.0", path = "../libs/substrate-differ" }
wasm-loader = { version = "0.18.0", path = "../libs/wasm-loader" }
wasm-testbed = { version = "0.18.0", path = "../libs/wasm-testbed" }
sp-version = { tag = "monthly-2023-01", git = "https://github.com/paritytech/substrate" }
hex = "0.4"
26 changes: 14 additions & 12 deletions lib/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use color_eyre::eyre::eyre;
use std::path::Path;
use std::{fs::File, path::PathBuf};
use std::{io::prelude::*, str::FromStr};
Expand All @@ -17,6 +18,7 @@ mod subwasm;
mod types;
pub use chain_info::*;
use log::{debug, info};
pub use metadata_wrapper::OutputFormat;
pub use runtime_info::*;
pub use subwasm::*;
pub use types::*;
Expand Down Expand Up @@ -67,7 +69,7 @@ pub fn download_runtime(url: &str, block_ref: Option<BlockRef>, output: Option<P
let url = match url {
url if url.starts_with("ws") => NodeEndpoint::WebSocket(url.to_string()),
url if url.starts_with("http") => NodeEndpoint::Http(url.to_string()),
_ => panic!("The url should either start with http or ws"),
_ => return Err(eyre!("The url should either start with http or ws")),
};

let reference = OnchainBlock { endpoint: url, block_ref };
Expand Down Expand Up @@ -129,41 +131,41 @@ pub fn diff(src_a: Source, src_b: Source) {

/// Compress a given runtime into a new file. You cannot compress
/// a runtime that is already compressed.
pub fn compress(input: PathBuf, output: PathBuf) -> Result<(), String> {
let wasm = WasmLoader::load_from_source(&Source::File(input)).unwrap();
pub fn compress(input: PathBuf, output: PathBuf) -> color_eyre::Result<()> {
let wasm = WasmLoader::load_from_source(&Source::File(input))?;

if wasm.compression().compressed() {
return Err("The input is already compressed".into());
return Err(eyre!("The input is already compressed"));
}

let bytes_compressed = Compression::compress(wasm.original_bytes()).unwrap();
let bytes_compressed = Compression::compress(wasm.original_bytes()).map_err(|e| eyre!(e))?;

debug!("original = {:?}", wasm.original_bytes().len());
debug!("compressed = {:?}", bytes_compressed.len());
info!("Saving compressed runtime to {:?}", output);

let mut buffer = File::create(output).unwrap();
buffer.write_all(&bytes_compressed.to_vec()).unwrap();
let mut buffer = File::create(output)?;
buffer.write_all(&bytes_compressed.to_vec())?;

Ok(())
}

/// Decompress a given runtime file. It is fine decompressing an already
/// decompressed runtime, you will just get the same.
pub fn decompress(input: PathBuf, output: PathBuf) -> Result<(), String> {
let wasm = WasmLoader::load_from_source(&Source::File(input)).unwrap();
pub fn decompress(input: PathBuf, output: PathBuf) -> color_eyre::Result<()> {
let wasm = WasmLoader::load_from_source(&Source::File(input))?;

let bytes_decompressed = match wasm.compression().compressed() {
false => wasm.original_bytes().clone(),
true => Compression::decompress(wasm.original_bytes()).unwrap(),
true => Compression::decompress(wasm.original_bytes()).map_err(|e| eyre!(e))?,
};

debug!("original = {:?}", wasm.original_bytes().len());
debug!("decompressed = {:?}", bytes_decompressed.len());

info!("Saving decompressed runtime to {:?}", output);
let mut buffer = File::create(output).unwrap();
buffer.write_all(&bytes_decompressed.to_vec()).unwrap();
let mut buffer = File::create(output)?;
buffer.write_all(&bytes_decompressed.to_vec())?;

Ok(())
}
Loading

0 comments on commit cfed868

Please sign in to comment.