From 82e662890f70170513ec3ce3ee825038e5f339f4 Mon Sep 17 00:00:00 2001 From: Vadim Anufriev Date: Tue, 6 Jan 2026 22:10:00 +0400 Subject: [PATCH 1/3] feat: add command for completions --- CHANGELOG.md | 3 ++- Cargo.lock | 10 ++++++++++ Cargo.toml | 1 + src/commands.rs | 33 +++++++++++++++++++++++++++++++++ src/handlers.rs | 11 +++++++++++ 5 files changed, 57 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55a95b7..24a1580 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,12 @@ page. See [DEVELOPMENT_CYCLE.md](DEVELOPMENT_CYCLE.md) for more details. ## [Unreleased] - Add wallet subcommand `config` to save wallet configs - Add `wallets` command to list all wallets saved configs +- Added `completions` subcommand to generate shell completions ## [2.0.0] - Removed MSRV and bumped Rust Edition to 2024 -- Add `--pretty` flag for formatting outputs in human-readable form +- Added `--pretty` flag for formatting outputs in human-readable form - Updated `bdk_wallet ` to `2.1.0`, `bdk_bitcoind_rpc` to `0.21.0`, `bdk_esplora` to `0.22.1`, `bdk_kyoto` to `0.13.1` - Updated `tracing-subscriber` to 0.3.20 - Added `tr` script type to `compile` command to support creating taproot descriptors diff --git a/Cargo.lock b/Cargo.lock index 98036e6..f126cfc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -203,6 +203,7 @@ dependencies = [ "bdk_redb", "bdk_wallet", "clap", + "clap_complete", "cli-table", "dirs", "env_logger", @@ -721,6 +722,15 @@ dependencies = [ "strsim", ] +[[package]] +name = "clap_complete" +version = "4.5.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c0da80818b2d95eca9aa614a30783e42f62bf5fdfee24e68cfb960b071ba8d1" +dependencies = [ + "clap", +] + [[package]] name = "clap_derive" version = "4.5.49" diff --git a/Cargo.toml b/Cargo.toml index b7d610a..cf6bd78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ license = "MIT" [dependencies] bdk_wallet = { version = "2.1.0", features = ["rusqlite", "keys-bip39", "compiler", "std"] } clap = { version = "4.5", features = ["derive","env"] } +clap_complete = "4.5" dirs = { version = "6.0.0" } env_logger = "0.11.6" log = "0.4" diff --git a/src/commands.rs b/src/commands.rs index 14ad9ea..96f9b88 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -18,6 +18,7 @@ use bdk_wallet::bitcoin::{ bip32::{DerivationPath, Xpriv}, }; use clap::{Args, Parser, Subcommand, ValueEnum, value_parser}; +use clap_complete::Shell; #[cfg(any(feature = "electrum", feature = "esplora", feature = "rpc"))] use crate::utils::parse_proxy_auth; @@ -127,7 +128,39 @@ pub enum CliSubCommand { }, /// List all saved wallet configurations. Wallets, + /// Generate tab-completion scripts for your shell. + /// + /// Outputs a shell-specific completion script to stdout. + /// To enable completions you need to redirect this output into the appropriate location for your shell. + /// + /// Bash: + /// bdk-cli completions bash > ~/.local/share/bash-completion/completions/bdk-cli + /// + /// Zsh: + /// bdk-cli completions zsh > ~/.zfunc/_bdk-cli + /// # Make sure ~/.zfunc is in your fpath (add to .zshrc): + /// # fpath=(~/.zfunc $fpath) + /// # autoload -Uz compinit && compinit + /// + /// Fish: + /// bdk-cli completions fish > ~/.config/fish/completions/bdk-cli.fish + /// + /// PowerShell: + /// bdk-cli completions powershell >> $PROFILE + /// + /// Elvish: + /// bdk-cli completions elvish >> ~/.elvish/rc.elv + /// + /// After installing the completion script, restart your shell or source + /// the configuration file for the changes to take effect. + #[command(verbatim_doc_comment)] + Completions { + /// Target shell syntax + #[arg(value_enum)] + shell: Shell, + }, } + /// Wallet operation subcommands. #[derive(Debug, Subcommand, Clone, PartialEq)] pub enum WalletSubCommand { diff --git a/src/handlers.rs b/src/handlers.rs index 1f867b4..0462e90 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -44,6 +44,7 @@ use bdk_wallet::{ descriptor::{Descriptor, Legacy, Miniscript}, miniscript::{Tap, descriptor::TapTree, policy::Concrete}, }; +use clap::CommandFactory; use cli_table::{Cell, CellStruct, Style, Table, format::Justify}; use serde_json::json; #[cfg(feature = "cbf")] @@ -1422,6 +1423,16 @@ pub(crate) async fn handle_command(cli_opts: CliOpts) -> Result { let descriptor = handle_descriptor_command(cli_opts.network, desc_type, key, pretty)?; Ok(descriptor) } + CliSubCommand::Completions { shell } => { + clap_complete::generate( + shell, + &mut CliOpts::command(), + "bdk-cli", + &mut std::io::stdout(), + ); + + Ok("".to_string()) + } }; result } From 7c1303ad9544a578887c12809b18f1c7d0ecc068 Mon Sep 17 00:00:00 2001 From: Vadim Anufriev Date: Sat, 7 Feb 2026 23:10:08 +0400 Subject: [PATCH 2/3] chore(clippy): resolve clippy warngins --- src/payjoin/mod.rs | 17 ++++++++--------- src/utils.rs | 2 +- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/payjoin/mod.rs b/src/payjoin/mod.rs index f5e1274..5d1efc0 100644 --- a/src/payjoin/mod.rs +++ b/src/payjoin/mod.rs @@ -75,7 +75,7 @@ impl<'a> PayjoinManager<'a> { let persister = payjoin::persist::NoopSessionPersister::::default(); let checked_max_fee_rate = max_fee_rate - .map(|rate| FeeRate::from_sat_per_kwu(rate)) + .map(FeeRate::from_sat_per_kwu) .unwrap_or(FeeRate::BROADCAST_MIN); let receiver = payjoin::receive::v2::ReceiverBuilder::new( @@ -296,7 +296,7 @@ impl<'a> PayjoinManager<'a> { .await } ReceiveSession::HasReplyableError(error) => self.handle_error(error, persister).await, - ReceiveSession::Closed(_) => return Err(Error::Generic("Session closed".to_string())), + ReceiveSession::Closed(_) => Err(Error::Generic("Session closed".to_string())), } } @@ -334,7 +334,7 @@ impl<'a> PayjoinManager<'a> { Err(e) => { return Err(Error::Generic(format!( "Error occurred when polling for Payjoin proposal from the directory: {}", - e.to_string() + e ))); } } @@ -637,16 +637,15 @@ impl<'a> PayjoinManager<'a> { return Err(ImplementationError::from("Cannot find the transaction in the mempool or the blockchain")); }; - let is_seen = match tx_details.chain_position { - bdk_wallet::chain::ChainPosition::Confirmed { .. } => true, - bdk_wallet::chain::ChainPosition::Unconfirmed { first_seen: Some(_), .. } => true, - _ => false - }; + let is_seen = matches!( + tx_details.chain_position, bdk_wallet::chain::ChainPosition::Confirmed { .. } + | bdk_wallet::chain::ChainPosition::Unconfirmed { first_seen: Some(_), .. } + ); if is_seen { return Ok(Some(tx_details.tx.as_ref().clone())); } - return Err(ImplementationError::from("Cannot find the transaction in the mempool or the blockchain")); + Err(ImplementationError::from("Cannot find the transaction in the mempool or the blockchain")) }, |outpoint| { let utxo = self.wallet.get_utxo(outpoint); diff --git a/src/utils.rs b/src/utils.rs index 73d3453..4514bb5 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -187,7 +187,7 @@ pub(crate) fn new_blockchain_client( } #[cfg(feature = "esplora")] ClientType::Esplora => { - let client = bdk_esplora::esplora_client::Builder::new(&url).build_async()?; + let client = bdk_esplora::esplora_client::Builder::new(url).build_async()?; BlockchainClient::Esplora { client: Box::new(client), parallel_requests: wallet_opts.parallel_requests, From c447159ef23bae5d0c54f7188d579d75379632be Mon Sep 17 00:00:00 2001 From: Vadim Anufriev Date: Mon, 9 Feb 2026 00:18:11 +0400 Subject: [PATCH 3/3] chore(clippy): fix clippy warnings again --- src/error.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/error.rs b/src/error.rs index 064a928..6520d1b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -63,7 +63,7 @@ pub enum BDKCliError { #[cfg(feature = "redb")] #[error("Redb StoreError: {0}")] - RedbStoreError(#[from] bdk_redb::error::StoreError), + RedbStoreError(Box), #[cfg(feature = "redb")] #[error("Redb dabtabase error: {0}")] @@ -119,3 +119,10 @@ impl From for BDKCliError { BDKCliError::PsbtExtractTxError(Box::new(value)) } } + +#[cfg(feature = "redb")] +impl From for BDKCliError { + fn from(err: bdk_redb::error::StoreError) -> Self { + BDKCliError::RedbStoreError(Box::new(err)) + } +}