use clap::{Args, Subcommand};
use kobe::{DerivedAccount, Wallet};
use crate::output::{self, AccountOutput, HdWalletOutput};
pub(crate) type CliResult<T = ()> = Result<T, Box<dyn std::error::Error>>;
#[derive(Args, Debug, Clone)]
pub(crate) struct SimpleArgs {
#[arg(short, long, default_value_t = 12)]
pub words: usize,
#[arg(short, long)]
pub passphrase: Option<String>,
#[arg(short, long, default_value_t = 1)]
pub count: u32,
#[arg(long)]
pub qr: bool,
}
impl SimpleArgs {
pub(crate) fn build_wallet(&self, mnemonic: Option<&str>) -> CliResult<Wallet> {
match mnemonic {
None => Ok(Wallet::generate(self.words, self.passphrase.as_deref())?),
Some(phrase) => {
let expanded = kobe::mnemonic::expand(phrase)?;
Ok(Wallet::from_mnemonic(
&expanded,
self.passphrase.as_deref(),
)?)
}
}
}
}
#[derive(Subcommand, Debug)]
pub(crate) enum SimpleSubcommand {
New {
#[command(flatten)]
args: SimpleArgs,
},
Import {
#[arg(short, long)]
mnemonic: String,
#[command(flatten)]
args: SimpleArgs,
},
}
impl SimpleSubcommand {
pub(crate) fn execute<F>(self, chain: &'static str, json: bool, derive_fn: F) -> CliResult
where
F: FnOnce(&Wallet, u32) -> CliResult<Vec<DerivedAccount>>,
{
self.execute_with(chain, json, derive_fn, |a| {
a.private_key_hex().as_str().to_owned()
})
}
pub(crate) fn execute_with<A, F, G>(
self,
chain: &'static str,
json: bool,
derive_fn: F,
format_private_key: G,
) -> CliResult
where
A: AsRef<DerivedAccount>,
F: FnOnce(&Wallet, u32) -> CliResult<Vec<A>>,
G: Fn(&A) -> String,
{
let (mnemonic, args) = match self {
Self::New { args } => (None, args),
Self::Import { mnemonic, args } => (Some(mnemonic), args),
};
let wallet = args.build_wallet(mnemonic.as_deref())?;
let accounts = derive_fn(&wallet, args.count)?;
let out = HdWalletOutput {
chain,
network: None,
address_type: None,
mnemonic: wallet.mnemonic().to_owned(),
passphrase_protected: wallet.has_passphrase(),
derivation_style: None,
accounts: accounts
.iter()
.enumerate()
.map(|(i, a)| {
let da = a.as_ref();
AccountOutput {
index: u32::try_from(i).unwrap_or(u32::MAX),
derivation_path: da.path().to_owned(),
address: da.address().to_owned(),
private_key: format_private_key(a),
}
})
.collect(),
};
output::render_hd_wallet(&out, json, args.qr)?;
Ok(())
}
}