From: Vihiga Tyonum Date: Tue, 26 May 2026 08:27:26 +0000 (+0100) Subject: refactor(output): apply generics to output mod X-Git-Url: http://internal-gitweb-vhost/blockdata/script/encode/-script/display/struct.DisplayArray.html?a=commitdiff_plain;h=5ced5aa764407d0b117b43ddeb6a9addfa15c8ed;p=bdk-cli refactor(output): apply generics to output mod --- diff --git a/src/commands.rs b/src/commands.rs index 52c2404..226e447 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -34,7 +34,7 @@ use crate::handlers::{ ))] use crate::{ client::ClientType, - online::{ + handlers::online::{ BroadcastCommand, FullScanCommand, ReceivePayjoinCommand, SendPayjoinCommand, SyncCommand, }, }; diff --git a/src/handlers/key.rs b/src/handlers/key.rs index 3f175d7..b9dbe34 100644 --- a/src/handlers/key.rs +++ b/src/handlers/key.rs @@ -17,13 +17,15 @@ use clap::Parser; impl KeySubCommand { pub fn execute(&self, ctx: &mut AppContext) -> Result<(), Error> { match self { - KeySubCommand::Generate(generate_key_command) => { - generate_key_command.execute(ctx)?.print() - } - KeySubCommand::Restore(restore_key_command) => { - restore_key_command.execute(ctx)?.print() - } - KeySubCommand::Derive(derive_key_command) => derive_key_command.execute(ctx)?.print(), + KeySubCommand::Generate(generate_key_command) => generate_key_command + .execute(ctx)? + .write_out(std::io::stdout()), + KeySubCommand::Restore(restore_key_command) => restore_key_command + .execute(ctx)? + .write_out(std::io::stdout()), + KeySubCommand::Derive(derive_key_command) => derive_key_command + .execute(ctx)? + .write_out(std::io::stdout()), } } } diff --git a/src/handlers/offline.rs b/src/handlers/offline.rs index 62d89d5..f541f74 100644 --- a/src/handlers/offline.rs +++ b/src/handlers/offline.rs @@ -1,11 +1,11 @@ use crate::commands::OfflineWalletSubCommand; use crate::error::BDKCliError as Error; use crate::handlers::{AppCommand, AppContext}; -use crate::utils::output::FormatOutput; +use crate::utils::output::{FormatOutput, ListResult}; use crate::utils::parse_address; use crate::utils::types::{ AddressResult, BalanceResult, KeychainPair, PsbtResult, RawPsbt, TransactionDetails, - TransactionListResult, UnspentDetails, UnspentListResult, + UnspentDetails, }; use crate::utils::{parse_outpoint, parse_recipient}; use bdk_wallet::bitcoin::base64::Engine; @@ -27,31 +27,47 @@ use { impl OfflineWalletSubCommand { pub fn execute(&self, ctx: &mut AppContext<'_>) -> Result<(), Error> { match self { - Self::NewAddress(new_address) => new_address.execute(ctx)?.print(), - Self::Balance(balance) => balance.execute(ctx)?.print(), - Self::UnusedAddress(unused_address_command) => { - unused_address_command.execute(ctx)?.print() + Self::NewAddress(new_address) => new_address.execute(ctx)?.write_out(std::io::stdout()), + Self::Balance(balance) => balance.execute(ctx)?.write_out(std::io::stdout()), + Self::UnusedAddress(unused_address_command) => unused_address_command + .execute(ctx)? + .write_out(std::io::stdout()), + Self::Unspent(unspent_command) => { + unspent_command.execute(ctx)?.write_out(std::io::stdout()) } - Self::Unspent(unspent_command) => unspent_command.execute(ctx)?.print(), - Self::Transactions(transactions_command) => transactions_command.execute(ctx)?.print(), - Self::CreateTx(createtx_command) => createtx_command.execute(ctx)?.print(), - Self::BumpFee(bumpfee_command) => bumpfee_command.execute(ctx)?.print(), - Self::Policies(policies_command) => policies_command.execute(ctx)?.print(), - Self::PublicDescriptor(public_descriptor_command) => { - public_descriptor_command.execute(ctx)?.print() + Self::Transactions(transactions_command) => transactions_command + .execute(ctx)? + .write_out(std::io::stdout()), + Self::CreateTx(createtx_command) => { + createtx_command.execute(ctx)?.write_out(std::io::stdout()) } - Self::Sign(sign_command) => sign_command.execute(ctx)?.print(), - Self::ExtractPsbt(extract_psbt_command) => extract_psbt_command.execute(ctx)?.print(), - Self::FinalizePsbt(finalize_psbt_command) => { - finalize_psbt_command.execute(ctx)?.print() + Self::BumpFee(bumpfee_command) => { + bumpfee_command.execute(ctx)?.write_out(std::io::stdout()) } - Self::CombinePsbt(combine_psbt_command) => combine_psbt_command.execute(ctx)?.print(), + Self::Policies(policies_command) => { + policies_command.execute(ctx)?.write_out(std::io::stdout()) + } + Self::PublicDescriptor(public_descriptor_command) => public_descriptor_command + .execute(ctx)? + .write_out(std::io::stdout()), + Self::Sign(sign_command) => sign_command.execute(ctx)?.write_out(std::io::stdout()), + Self::ExtractPsbt(extract_psbt_command) => extract_psbt_command + .execute(ctx)? + .write_out(std::io::stdout()), + Self::FinalizePsbt(finalize_psbt_command) => finalize_psbt_command + .execute(ctx)? + .write_out(std::io::stdout()), + Self::CombinePsbt(combine_psbt_command) => combine_psbt_command + .execute(ctx)? + .write_out(std::io::stdout()), #[cfg(feature = "bip322")] - Self::SignMessage(sign_message_command) => sign_message_command.execute(ctx)?.print(), + Self::SignMessage(sign_message_command) => sign_message_command + .execute(ctx)? + .write_out(std::io::stdout()), #[cfg(feature = "bip322")] - Self::VerifyMessage(verify_message_command) => { - verify_message_command.execute(ctx)?.print() - } + Self::VerifyMessage(verify_message_command) => verify_message_command + .execute(ctx)? + .write_out(std::io::stdout()), } } } @@ -92,7 +108,7 @@ impl AppCommand for UnusedAddressCommand { pub struct UnspentCommand {} impl AppCommand for UnspentCommand { - type Output = UnspentListResult; + type Output = ListResult; fn execute(&self, ctx: &mut AppContext) -> Result { let wallet = ctx @@ -104,7 +120,7 @@ impl AppCommand for UnspentCommand { .map(|utxo| UnspentDetails::from_local_output(&utxo, ctx.network)) .collect(); - Ok(UnspentListResult(utxos)) + Ok(ListResult::new(utxos)) } } @@ -112,7 +128,7 @@ impl AppCommand for UnspentCommand { pub struct TransactionsCommand {} impl AppCommand for TransactionsCommand { - type Output = TransactionListResult; + type Output = ListResult; fn execute(&self, ctx: &mut AppContext) -> Result { let wallet = ctx @@ -147,7 +163,7 @@ impl AppCommand for TransactionsCommand { }) .collect(); - Ok(TransactionListResult(txns)) + Ok(ListResult::new(txns)) } } diff --git a/src/handlers/online.rs b/src/handlers/online.rs index eb12581..866f6f9 100644 --- a/src/handlers/online.rs +++ b/src/handlers/online.rs @@ -46,17 +46,24 @@ impl OnlineWalletSubCommand { pub async fn execute(&self, ctx: &mut AppContext<'_>) -> Result<(), Error> { match self { OnlineWalletSubCommand::FullScan(full_scan_command) => { - full_scan_command.execute(ctx).await?.print() + let response: StatusResult = full_scan_command.execute(ctx).await?; + response.write_out(std::io::stdout()) + } + OnlineWalletSubCommand::Sync(sync_command) => { + let response: StatusResult = sync_command.execute(ctx).await?; + response.write_out(std::io::stdout()) } - OnlineWalletSubCommand::Sync(sync_command) => sync_command.execute(ctx).await?.print(), OnlineWalletSubCommand::Broadcast(broadcast_command) => { - broadcast_command.execute(ctx).await?.print() + let response: TransactionResult = broadcast_command.execute(ctx).await?; + response.write_out(std::io::stdout()) } OnlineWalletSubCommand::ReceivePayjoin(receive_payjoin_command) => { - receive_payjoin_command.execute(ctx).await?.print() + let response: StatusResult = receive_payjoin_command.execute(ctx).await?; + response.write_out(std::io::stdout()) } OnlineWalletSubCommand::SendPayjoin(send_payjoin_command) => { - send_payjoin_command.execute(ctx).await?.print() + let response: StatusResult = send_payjoin_command.execute(ctx).await?; + response.write_out(std::io::stdout()) } } } @@ -299,7 +306,6 @@ pub struct BroadcastCommand { feature = "cbf", feature = "rpc" ))] - impl AsyncCommand for BroadcastCommand { type Output = TransactionResult; diff --git a/src/handlers/repl.rs b/src/handlers/repl.rs index 6f9bd52..19206b2 100644 --- a/src/handlers/repl.rs +++ b/src/handlers/repl.rs @@ -1,6 +1,6 @@ use bdk_wallet::{Wallet, bitcoin::Network}; -// #[cfg(feature = "repl")] +#[cfg(feature = "repl")] use crate::handlers::{AppCommand, AppContext}; use crate::utils::output::FormatOutput; @@ -56,17 +56,20 @@ pub(crate) async fn respond( } WalletSubCommand::Config(config_cmd) => { let mut ctx = AppContext::new(network, datadir); - let res = config_cmd + let _ = config_cmd .execute(&mut ctx) .map_err(|e| e.to_string())? - .print(); + .write_out(std::io::stdout()); Some(()) } }, // Assuming your REPL Descriptor command is an inline struct based on commands.rs ReplSubCommand::Descriptor(cmd) => { - let value = cmd.execute(&mut ctx).map_err(|e| e.to_string())?.print(); + let value = cmd + .execute(&mut ctx) + .map_err(|e| e.to_string())? + .write_out(std::io::stdout()); Some(()) } diff --git a/src/main.rs b/src/main.rs index f8877a6..99f480b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -151,7 +151,7 @@ async fn run(cli_opts: CliOpts) -> Result<(), Error> { let mut ctx = AppContext::new(cli_opts.network, home_dir); - config_cmd.execute(&mut ctx)?.print()?; + config_cmd.execute(&mut ctx)?.write_out(std::io::stdout())?; } }, @@ -161,11 +161,13 @@ async fn run(cli_opts: CliOpts) -> Result<(), Error> { } CliSubCommand::Descriptor(descriptor_command) => { let mut ctx = AppContext::new(cli_opts.network, home_dir); - descriptor_command.execute(&mut ctx)?.print()?; + descriptor_command + .execute(&mut ctx)? + .write_out(std::io::stdout())?; } CliSubCommand::Wallets(cmd) => { let mut ctx = AppContext::new(cli_opts.network, home_dir); - cmd.execute(&mut ctx)?.print()?; + cmd.execute(&mut ctx)?.write_out(std::io::stdout())?; } CliSubCommand::Repl { wallet: _ } => todo!(), CliSubCommand::Completions { shell } => { @@ -174,7 +176,7 @@ async fn run(cli_opts: CliOpts) -> Result<(), Error> { #[cfg(feature = "compiler")] CliSubCommand::Compile(cmd) => { let mut ctx = AppContext::new(cli_opts.network, home_dir); - cmd.execute(&mut ctx)?.print()?; + cmd.execute(&mut ctx)?.write_out(std::io::stdout())?; } }; diff --git a/src/utils/output.rs b/src/utils/output.rs index 3f9a5e9..b2cd32a 100644 --- a/src/utils/output.rs +++ b/src/utils/output.rs @@ -1,3 +1,5 @@ +use std::io::Write; + use crate::error::BDKCliError as Error; use serde::Serialize; @@ -8,9 +10,28 @@ pub trait FormatOutput: Serialize { .map_err(|e| Error::Generic(format!("JSON serialization failed: {e}"))) } - fn print(&self) -> Result<(), Error> { + fn write_out(&self, mut writer: W) -> Result<(), Error> { let output = self.format()?; - println!("{}", output); - Ok(()) + + writeln!(writer, "{}", output) + .map_err(|e| Error::Generic(format!("Failed to write output: {e}"))) + } +} + +impl FormatOutput for T {} + +/// A generic wrapper for commands that return a list of items. +#[derive(Serialize)] +pub struct ListResult { + pub count: usize, + pub items: Vec, +} + +impl ListResult { + pub fn new(items: Vec) -> Self { + Self { + count: items.len(), + items, + } } } diff --git a/src/utils/types.rs b/src/utils/types.rs index 2fa16b9..5135343 100644 --- a/src/utils/types.rs +++ b/src/utils/types.rs @@ -1,7 +1,6 @@ use std::collections::HashMap; use crate::config::WalletConfigInner; -use crate::utils::output::FormatOutput; use crate::utils::shorten; use bdk_wallet::Balance; use bdk_wallet::bitcoin::{ @@ -27,8 +26,6 @@ impl From for AddressResult { } } -impl FormatOutput for AddressResult {} - /// Represents the data for a single transaction #[derive(Serialize)] pub struct TransactionDetails { @@ -49,13 +46,6 @@ pub struct TransactionDetails { pub total_value: u64, } -/// A wrapper type for a list of transactions. -#[derive(Serialize)] -#[serde(transparent)] -pub struct TransactionListResult(pub Vec); - -impl FormatOutput for TransactionListResult {} - /// single UTXO #[derive(Serialize)] pub struct UnspentDetails { @@ -99,13 +89,6 @@ impl UnspentDetails { } } -/// Wrapper for the list of UTXOs -#[derive(Serialize)] -#[serde(transparent)] -pub struct UnspentListResult(pub Vec); - -impl FormatOutput for UnspentListResult {} - #[derive(Serialize)] pub struct PsbtResult { pub psbt: String, @@ -131,8 +114,6 @@ impl PsbtResult { } } -impl FormatOutput for PsbtResult {} - #[derive(Serialize)] pub struct RawPsbt { pub raw_tx: String, @@ -146,19 +127,12 @@ impl RawPsbt { } } -impl FormatOutput for RawPsbt {} - #[derive(Serialize)] pub struct KeychainPair { pub external: T, pub internal: T, } -impl FormatOutput for KeychainPair {} - -// Table formatting for JSON value pairs (used by Policies) -impl FormatOutput for KeychainPair {} - #[derive(Serialize, Debug, Default)] pub struct MessageResult { #[serde(skip_serializing_if = "Option::is_none")] @@ -171,8 +145,6 @@ pub struct MessageResult { pub proven_amount: Option, } -impl FormatOutput for MessageResult {} - #[derive(Serialize, Debug)] pub struct StatusResult { pub message: String, @@ -186,22 +158,16 @@ impl StatusResult { } } -impl FormatOutput for StatusResult {} - #[derive(Serialize, Debug)] pub struct TransactionResult { pub txid: String, } -impl FormatOutput for TransactionResult {} - /// Return type definition #[derive(Serialize)] #[serde(transparent)] pub struct WalletsListResult(pub HashMap); -impl FormatOutput for WalletsListResult {} - /// return type #[derive(Serialize)] pub struct DescriptorResult { @@ -224,8 +190,6 @@ pub struct DescriptorResult { pub fingerprint: Option, } -impl FormatOutput for DescriptorResult {} - #[derive(Serialize)] pub struct KeyResult { pub xprv: String, @@ -240,8 +204,6 @@ pub struct KeyResult { pub fingerprint: Option, } -impl FormatOutput for KeyResult {} - /// Balance representation #[derive(Serialize)] pub struct BalanceResult { @@ -263,5 +225,3 @@ impl From for BalanceResult { } } } - -impl FormatOutput for BalanceResult {}