Update cli module to use StructOpt and add docs

This commit is contained in:
Steve Myers 2020-11-30 22:03:39 -08:00
parent 2e7f98a371
commit 364b47bfcb
No known key found for this signature in database
GPG Key ID: 8105A46B22C2D051
3 changed files with 748 additions and 620 deletions

View File

@ -27,6 +27,7 @@ cc = { version = "=1.0.62", optional = true }
socks = { version = "0.3", optional = true } socks = { version = "0.3", optional = true }
lazy_static = { version = "1.4", optional = true } lazy_static = { version = "1.4", optional = true }
tiny-bip39 = { version = "^0.8", optional = true } tiny-bip39 = { version = "^0.8", optional = true }
structopt = { version = "^0.3", optional = true }
# Platform-specific dependencies # Platform-specific dependencies
[target.'cfg(not(target_arch = "wasm32"))'.dependencies] [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
@ -45,7 +46,7 @@ electrum = ["electrum-client"]
esplora = ["reqwest", "futures"] esplora = ["reqwest", "futures"]
compact_filters = ["rocksdb", "socks", "lazy_static", "cc"] compact_filters = ["rocksdb", "socks", "lazy_static", "cc"]
key-value-db = ["sled"] key-value-db = ["sled"]
cli-utils = ["clap", "base64"] cli-utils = ["clap", "base64", "structopt"]
async-interface = ["async-trait"] async-interface = ["async-trait"]
all-keys = ["keys-bip39"] all-keys = ["keys-bip39"]
keys-bip39 = ["tiny-bip39"] keys-bip39 = ["tiny-bip39"]

View File

@ -24,27 +24,34 @@
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use bitcoin::Network;
use clap::AppSettings;
use log::{debug, error, info, trace, warn, LevelFilter};
use rustyline::error::ReadlineError; use rustyline::error::ReadlineError;
use rustyline::Editor; use rustyline::Editor;
use structopt::StructOpt;
use clap::AppSettings;
#[allow(unused_imports)]
use log::{debug, error, info, trace, LevelFilter};
use bitcoin::Network;
use bdk::bitcoin; use bdk::bitcoin;
use bdk::blockchain::esplora::EsploraBlockchainConfig;
use bdk::blockchain::{ use bdk::blockchain::{
AnyBlockchain, AnyBlockchainConfig, ConfigurableBlockchain, ElectrumBlockchainConfig, AnyBlockchain, AnyBlockchainConfig, ConfigurableBlockchain, ElectrumBlockchainConfig,
}; };
use bdk::cli; use bdk::cli::{self, WalletOpt, WalletSubCommand};
use bdk::sled; use bdk::sled;
use bdk::Wallet; use bdk::Wallet;
use bdk::blockchain::esplora::EsploraBlockchainConfig; #[derive(Debug, StructOpt, Clone, PartialEq)]
#[structopt(name = "BDK Wallet", setting = AppSettings::NoBinaryName,
version = option_env ! ("CARGO_PKG_VERSION").unwrap_or("unknown"),
author = option_env ! ("CARGO_PKG_AUTHORS").unwrap_or(""))]
struct ReplOpt {
/// Wallet sub-command
#[structopt(subcommand)]
pub subcommand: WalletSubCommand,
}
fn prepare_home_dir() -> PathBuf { fn prepare_home_dir() -> PathBuf {
let mut dir = PathBuf::new(); let mut dir = PathBuf::new();
@ -61,100 +68,96 @@ fn prepare_home_dir() -> PathBuf {
} }
fn main() { fn main() {
env_logger::init(); let cli_opt: WalletOpt = WalletOpt::from_args();
let app = cli::make_cli_subcommands(); let level = LevelFilter::from_str(cli_opt.log_level.as_str()).unwrap_or(LevelFilter::Info);
let mut repl_app = app.clone().setting(AppSettings::NoBinaryName); env_logger::builder().filter_level(level).init();
let app = cli::add_global_flags(app); let network = Network::from_str(cli_opt.network.as_str()).unwrap_or(Network::Testnet);
debug!("network: {:?}", network);
if network == Network::Bitcoin {
warn!("This is experimental software and not currently recommended for use on Bitcoin mainnet, proceed with caution.")
}
let matches = app.get_matches(); let descriptor = cli_opt.descriptor.as_str();
let change_descriptor = cli_opt.change_descriptor.as_deref();
// TODO
// let level = match matches.occurrences_of("v") {
// 0 => LevelFilter::Info,
// 1 => LevelFilter::Debug,
// _ => LevelFilter::Trace,
// };
let network = match matches.value_of("network") {
Some("regtest") => Network::Regtest,
Some("testnet") | _ => Network::Testnet,
};
let descriptor = matches.value_of("descriptor").unwrap();
let change_descriptor = matches.value_of("change_descriptor");
debug!("descriptors: {:?} {:?}", descriptor, change_descriptor); debug!("descriptors: {:?} {:?}", descriptor, change_descriptor);
let database = sled::open(prepare_home_dir().to_str().unwrap()).unwrap(); let database = sled::open(prepare_home_dir().to_str().unwrap()).unwrap();
let tree = database let tree = database.open_tree(cli_opt.wallet).unwrap();
.open_tree(matches.value_of("wallet").unwrap())
.unwrap();
debug!("database opened successfully"); debug!("database opened successfully");
let config = match matches.value_of("esplora") { let config = match cli_opt.esplora {
Some(base_url) => AnyBlockchainConfig::Esplora(EsploraBlockchainConfig { Some(base_url) => AnyBlockchainConfig::Esplora(EsploraBlockchainConfig {
base_url: base_url.to_string(), base_url: base_url.to_string(),
concurrency: matches concurrency: Some(cli_opt.esplora_concurrency),
.value_of("esplora_concurrency")
.and_then(|v| v.parse::<u8>().ok()),
}), }),
None => AnyBlockchainConfig::Electrum(ElectrumBlockchainConfig { None => AnyBlockchainConfig::Electrum(ElectrumBlockchainConfig {
url: matches.value_of("server").unwrap().to_string(), url: cli_opt.electrum,
socks5: matches.value_of("proxy").map(ToString::to_string), socks5: cli_opt.proxy,
retry: 10, retry: 10,
timeout: 10, timeout: 10,
}), }),
}; };
let wallet = Arc::new(
Wallet::new(
descriptor,
change_descriptor,
network,
tree,
AnyBlockchain::from_config(&config).unwrap(),
)
.unwrap(),
);
if let Some(_sub_matches) = matches.subcommand_matches("repl") { let wallet = Wallet::new(
let mut rl = Editor::<()>::new(); descriptor,
change_descriptor,
network,
tree,
AnyBlockchain::from_config(&config).unwrap(),
)
.unwrap();
// if rl.load_history("history.txt").is_err() { let wallet = Arc::new(wallet);
// println!("No previous history.");
// }
loop { match cli_opt.subcommand {
let readline = rl.readline(">> "); WalletSubCommand::Other(external) if external.contains(&"repl".to_string()) => {
match readline { let mut rl = Editor::<()>::new();
Ok(line) => {
if line.trim() == "" { // if rl.load_history("history.txt").is_err() {
continue; // println!("No previous history.");
// }
loop {
let readline = rl.readline(">> ");
match readline {
Ok(line) => {
if line.trim() == "" {
continue;
}
rl.add_history_entry(line.as_str());
let split_line: Vec<&str> = line.split(" ").collect();
let repl_subcommand: Result<ReplOpt, clap::Error> =
ReplOpt::from_iter_safe(split_line);
debug!("repl_subcommand = {:?}", repl_subcommand);
if let Err(err) = repl_subcommand {
println!("{}", err.message);
continue;
}
let result = cli::handle_wallet_subcommand(
&Arc::clone(&wallet),
repl_subcommand.unwrap().subcommand,
)
.unwrap();
println!("{}", serde_json::to_string_pretty(&result).unwrap());
} }
Err(ReadlineError::Interrupted) => continue,
rl.add_history_entry(line.as_str()); Err(ReadlineError::Eof) => break,
let matches = repl_app.get_matches_from_safe_borrow(line.split(" ")); Err(err) => {
if let Err(err) = matches { println!("{:?}", err);
println!("{}", err.message); break;
continue;
} }
let result =
cli::handle_matches(&Arc::clone(&wallet), matches.unwrap()).unwrap();
println!("{}", serde_json::to_string_pretty(&result).unwrap());
}
Err(ReadlineError::Interrupted) => continue,
Err(ReadlineError::Eof) => break,
Err(err) => {
println!("{:?}", err);
break;
} }
} }
}
// rl.save_history("history.txt").unwrap(); // rl.save_history("history.txt").unwrap();
} else { }
let result = cli::handle_matches(&wallet, matches).unwrap(); _ => {
println!("{}", serde_json::to_string_pretty(&result).unwrap()); let result = cli::handle_wallet_subcommand(&wallet, cli_opt.subcommand).unwrap();
println!("{}", serde_json::to_string_pretty(&result).unwrap());
}
} }
} }

1202
src/cli.rs

File diff suppressed because it is too large Load Diff