#![deny(unused_crate_dependencies)]
mod cmd;
use self::cmd::{
BuildCommand,
CallCommand,
CheckCommand,
DecodeCommand,
ErrorVariant,
InstantiateCommand,
TestCommand,
UploadCommand,
};
use contract_build::{
name_value_println,
util::DEFAULT_KEY_COL_WIDTH,
OutputType,
};
use std::{
fmt::Display,
path::PathBuf,
str::FromStr,
};
use anyhow::{
anyhow,
Error,
Result,
};
use clap::{
Args,
Parser,
Subcommand,
};
use colored::Colorize;
#[cfg(test)]
use assert_cmd as _;
#[cfg(test)]
use predicates as _;
#[cfg(test)]
use regex as _;
#[cfg(test)]
use tempfile as _;
use which as _;
#[derive(Debug, Parser)]
#[clap(bin_name = "cargo")]
#[clap(version = env!("CARGO_CONTRACT_CLI_IMPL_VERSION"))]
pub(crate) enum Opts {
#[clap(name = "contract")]
#[clap(version = env!("CARGO_CONTRACT_CLI_IMPL_VERSION"))]
#[clap(action = ArgAction::DeriveDisplayOrder)]
Contract(ContractArgs),
}
#[derive(Debug, Args)]
pub(crate) struct ContractArgs {
#[clap(subcommand)]
cmd: Command,
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub(crate) struct HexData(pub Vec<u8>);
impl FromStr for HexData {
type Err = hex::FromHexError;
fn from_str(input: &str) -> std::result::Result<Self, Self::Err> {
hex::decode(input).map(HexData)
}
}
#[derive(Debug, Subcommand)]
enum Command {
#[clap(name = "new")]
New {
name: String,
#[clap(short, long, value_parser)]
target_dir: Option<PathBuf>,
},
#[clap(name = "build")]
Build(BuildCommand),
#[clap(name = "check")]
Check(CheckCommand),
#[clap(name = "test")]
Test(TestCommand),
#[clap(name = "upload")]
Upload(UploadCommand),
#[clap(name = "instantiate")]
Instantiate(InstantiateCommand),
#[clap(name = "call")]
Call(CallCommand),
#[clap(name = "decode")]
Decode(DecodeCommand),
}
fn main() {
tracing_subscriber::fmt::init();
let Opts::Contract(args) = Opts::parse();
match exec(args.cmd) {
Ok(()) => {}
Err(err) => {
eprintln!("{}", err);
std::process::exit(1);
}
}
}
fn exec(cmd: Command) -> Result<()> {
match &cmd {
Command::New { name, target_dir } => {
contract_build::new_contract_project(name, target_dir.as_ref())?;
println!("Created contract {}", name);
Ok(())
}
Command::Build(build) => {
let result = build.exec().map_err(format_err)?;
if matches!(result.output_type, OutputType::Json) {
println!("{}", result.serialize_json()?)
} else if result.verbosity.is_verbose() {
println!("{}", result.display())
}
Ok(())
}
Command::Check(check) => {
let res = check.exec().map_err(format_err)?;
assert!(
res.dest_wasm.is_none(),
"no dest_wasm must be on the generation result"
);
if res.verbosity.is_verbose() {
println!("\nYour contract's code was built successfully.")
}
Ok(())
}
Command::Test(test) => {
let res = test.exec().map_err(format_err)?;
if res.verbosity.is_verbose() {
println!("{}", res.display()?)
}
Ok(())
}
Command::Upload(upload) => {
upload
.run()
.map_err(|err| map_extrinsic_err(err, upload.is_json()))
}
Command::Instantiate(instantiate) => {
instantiate
.run()
.map_err(|err| map_extrinsic_err(err, instantiate.is_json()))
}
Command::Call(call) => {
call.run()
.map_err(|err| map_extrinsic_err(err, call.is_json()))
}
Command::Decode(decode) => decode.run().map_err(format_err),
}
}
fn map_extrinsic_err(err: ErrorVariant, is_json: bool) -> Error {
if is_json {
anyhow!(
"{}",
serde_json::to_string_pretty(&err)
.expect("error serialization is infallible; qed")
)
} else {
format_err(err)
}
}
fn format_err<E: Display>(err: E) -> Error {
anyhow!(
"{} {}",
"ERROR:".bright_red().bold(),
format!("{}", err).bright_red()
)
}