Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cli tool tests #977

Merged
merged 14 commits into from
Jun 14, 2023
Merged
18 changes: 7 additions & 11 deletions cli/src/commands/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use crate::utils::FileOrUrl;
use clap::Parser as ClapParser;
use color_eyre::eyre;
use color_eyre::eyre::eyre;
use subxt_codegen::{DerivesRegistry, TypeSubstitutes, TypeSubstitutionError};

/// Generate runtime API client code from metadata.
Expand Down Expand Up @@ -87,7 +88,7 @@ fn substitute_type_parser(src: &str) -> Result<(String, String), String> {
Ok((from.to_string(), to.to_string()))
}

pub async fn run(opts: Opts) -> color_eyre::Result<()> {
pub async fn run(opts: Opts, output: &mut impl std::io::Write) -> color_eyre::Result<()> {
let bytes = opts.file_or_url.fetch().await?;

codegen(
Expand All @@ -102,6 +103,7 @@ pub async fn run(opts: Opts) -> color_eyre::Result<()> {
opts.runtime_types_only,
opts.no_default_derives,
opts.no_default_substitutions,
output,
)?;
Ok(())
}
Expand All @@ -128,6 +130,7 @@ fn codegen(
runtime_types_only: bool,
no_default_derives: bool,
no_default_substitutions: bool,
output: &mut impl std::io::Write,
) -> color_eyre::Result<()> {
let item_mod = syn::parse_quote!(
pub mod api {}
Expand Down Expand Up @@ -190,15 +193,8 @@ fn codegen(
crate_path,
should_gen_docs,
runtime_types_only,
);
match runtime_api {
Ok(runtime_api) => println!("{runtime_api}"),
Err(e) => {
// Print the error directly to avoid implementing `Send + Sync` on `CodegenError`.
use color_eyre::owo_colors::OwoColorize;
println!("{}", e.to_string().red())
}
};

)
.map_err(|code_gen_err| eyre!("{code_gen_err}"))?;
writeln!(output, "{runtime_api}")?;
Ok(())
}
31 changes: 21 additions & 10 deletions cli/src/commands/compatibility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,21 @@ pub struct Opts {
version: MetadataVersion,
}

pub async fn run(opts: Opts) -> color_eyre::Result<()> {
pub async fn run(opts: Opts, output: &mut impl std::io::Write) -> color_eyre::Result<()> {
match opts.pallet {
Some(pallet) => {
handle_pallet_metadata(opts.nodes.as_slice(), pallet.as_str(), opts.version).await
handle_pallet_metadata(opts.nodes.as_slice(), pallet.as_str(), opts.version, output)
.await
}
None => handle_full_metadata(opts.nodes.as_slice(), opts.version).await,
None => handle_full_metadata(opts.nodes.as_slice(), opts.version, output).await,
}
}

async fn handle_pallet_metadata(
nodes: &[Uri],
name: &str,
version: MetadataVersion,
output: &mut impl std::io::Write,
) -> color_eyre::Result<()> {
#[derive(Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
Expand All @@ -67,7 +69,10 @@ async fn handle_pallet_metadata(
Some(pallet_metadata) => {
let hash = pallet_metadata.hash();
let hex_hash = hex::encode(hash);
println!("Node {node:?} has pallet metadata hash {hex_hash:?}");
writeln!(
output,
"Node {node:?} has pallet metadata hash {hex_hash:?}"
)?;

compatibility
.pallet_present
Expand All @@ -81,34 +86,40 @@ async fn handle_pallet_metadata(
}
}

println!(
writeln!(
output,
"\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], version: MetadataVersion) -> color_eyre::Result<()> {
async fn handle_full_metadata(
nodes: &[Uri],
version: MetadataVersion,
output: &mut impl std::io::Write,
) -> color_eyre::Result<()> {
let mut compatibility_map: HashMap<String, Vec<String>> = HashMap::new();
for node in nodes.iter() {
let metadata = fetch_runtime_metadata(node, version).await?;
let hash = metadata.hasher().hash();
let hex_hash = hex::encode(hash);
println!("Node {node:?} has metadata hash {hex_hash:?}",);
writeln!(output, "Node {node:?} has metadata hash {hex_hash:?}",)?;

compatibility_map
.entry(hex_hash)
.or_insert_with(Vec::new)
.push(node.to_string());
}

println!(
writeln!(
output,
"\nCompatible nodes\n{}",
serde_json::to_string_pretty(&compatibility_map)
.context("Failed to parse compatibility map")?
);
)?;

Ok(())
}
Expand Down
24 changes: 14 additions & 10 deletions cli/src/commands/explore/calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ use color_eyre::eyre::eyre;
use scale_info::form::PortableForm;
use scale_info::{PortableRegistry, Type, TypeDef, TypeDefVariant};
use scale_value::{Composite, ValueDef};
use std::fmt::Write;
use std::str::FromStr;
use std::write;

use subxt::tx;
use subxt::utils::H256;
Expand All @@ -26,10 +24,11 @@ pub struct CallsSubcommand {
trailing_args: Vec<String>,
}

pub(crate) fn explore_calls(
pub fn explore_calls(
command: CallsSubcommand,
metadata: &Metadata,
pallet_metadata: PalletMetadata,
output: &mut impl std::io::Write,
) -> color_eyre::Result<()> {
let pallet_name = pallet_metadata.name();

Expand All @@ -40,7 +39,7 @@ pub(crate) fn explore_calls(
// if no call specified, show user the calls to choose from:
let Some(call_name) = command.call else {
let available_calls = print_available_calls(calls_enum_type_def, pallet_name);
println!("Usage:\n subxt explore {pallet_name} calls <CALL>\n explore a specific call within this pallet\n\n{available_calls}", );
writeln!(output, "Usage:\n subxt explore {pallet_name} calls <CALL>\n explore a specific call within this pallet\n\n{available_calls}")?;
return Ok(());
};

Expand All @@ -60,13 +59,19 @@ pub(crate) fn explore_calls(
type_description = with_indent(type_description, 4);
let mut type_examples = print_type_examples(&call.fields, metadata.types(), "SCALE_VALUE")?;
type_examples = with_indent(type_examples, 4);
let mut output = String::new();
write!(output, "Usage:\n subxt explore {pallet_name} calls {call_name} <SCALE_VALUE>\n construct the call by providing a valid argument\n\n")?;
write!(
writeln!(output, "Usage:")?;
writeln!(
output,
" subxt explore {pallet_name} calls {call_name} <SCALE_VALUE>"
)?;
writeln!(
output,
" construct the call by providing a valid argument\n"
)?;
writeln!(
output,
"The call expect expects a <SCALE_VALUE> with this shape:\n{type_description}\n\n{}\n\nYou may need to surround the value in single quotes when providing it as an argument."
, &type_examples[4..])?;
println!("{output}");
return Ok(());
}

Expand All @@ -77,8 +82,7 @@ pub(crate) fn explore_calls(
let payload = tx::dynamic(pallet_name, call_name, value_as_composite);
let unsigned_extrinsic = offline_client.tx().create_unsigned(&payload)?;
let hex_bytes = format!("0x{}", hex::encode(unsigned_extrinsic.encoded()));
println!("Encoded call data:\n {hex_bytes}");

writeln!(output, "Encoded call data:\n {hex_bytes}")?;
Ok(())
}

Expand Down
30 changes: 16 additions & 14 deletions cli/src/commands/explore/constants.rs
Original file line number Diff line number Diff line change
@@ -1,62 +1,64 @@
use clap::Args;
use color_eyre::eyre::eyre;
use std::fmt::Write;
use std::write;

use subxt::metadata::{types::PalletMetadata, Metadata};

use crate::utils::type_description::print_type_description;
use crate::utils::{print_docs_with_indent, with_indent};
use crate::utils::{print_first_paragraph_with_indent, with_indent};

#[derive(Debug, Clone, Args)]
pub struct ConstantsSubcommand {
constant: Option<String>,
}

pub(crate) fn explore_constants(
pub fn explore_constants(
command: ConstantsSubcommand,
metadata: &Metadata,
pallet_metadata: PalletMetadata,
output: &mut impl std::io::Write,
) -> color_eyre::Result<()> {
let pallet_name = pallet_metadata.name();
let Some(constant_name) = command.constant else {
let available_constants = print_available_constants(pallet_metadata, pallet_name);
println!("Usage:\n subxt explore {pallet_name} constants <CONSTANT>\n explore a specific call within this pallet\n\n{available_constants}", );
writeln!(output, "Usage:")?;
writeln!(output, " subxt explore {pallet_name} constants <CONSTANT>")?;
writeln!(output, " explore a specific call within this pallet\n\n{available_constants}")?;
return Ok(());
};

// if specified constant is wrong, show user the constants to choose from (but this time as an error):
let Some(constant) = pallet_metadata.constants().find(|constant| constant.name().to_lowercase() == constant_name.to_lowercase()) else {
let available_constants = print_available_constants(pallet_metadata, pallet_name);
let description = format!("Usage:\n subxt explore {pallet_name} constants <CONSTANT>\n explore a specific call within this pallet\n\n{available_constants}", );
let mut description = "Usage:".to_string();
writeln!(description, " subxt explore {pallet_name} constants <CONSTANT>")?;
writeln!(description, " explore a specific call within this pallet\n\n{available_constants}")?;
let err = eyre!("constant \"{constant_name}\" not found in \"{pallet_name}\" pallet!\n\n{description}");
return Err(err);
};

// docs
let mut output = String::new();
let doc_string = print_docs_with_indent(constant.docs(), 4);
let doc_string = print_first_paragraph_with_indent(constant.docs(), 4);
if !doc_string.is_empty() {
write!(output, "Description:\n{doc_string}")?;
writeln!(output, "Description:\n{doc_string}")?;
}

// shape
let mut type_description = print_type_description(&constant.ty(), metadata.types())?;
type_description = with_indent(type_description, 4);
write!(
writeln!(
output,
"\n\nThe constant has the following shape:\n{type_description}"
"\nThe constant has the following shape:\n{type_description}"
)?;

// value
let scale_val =
scale_value::scale::decode_as_type(&mut constant.value(), constant.ty(), metadata.types())?;
write!(
writeln!(
output,
"\n\nThe value of the constant is:\n {}",
"\nThe value of the constant is:\n {}",
scale_value::stringify::to_string(&scale_val)
)?;

println!("{output}");
Ok(())
}

Expand Down
Loading