]> Untitled Git - bdk-cli/commitdiff
ref(utils): use types in desc output in utils
authorVihiga Tyonum <withtvpeter@gmail.com>
Wed, 29 Apr 2026 15:35:19 +0000 (16:35 +0100)
committerVihiga Tyonum <withtvpeter@gmail.com>
Fri, 5 Jun 2026 16:16:41 +0000 (17:16 +0100)
src/handlers/descriptor.rs
src/handlers/types.rs
src/utils/descriptors.rs
src/utils/output.rs

index 44f1954a8fb03f34fefc7d0e5a9b091775b6924a..9babbc8e3ebeacced98587cef7c6e7efeaea27a0 100644 (file)
@@ -2,17 +2,17 @@ use crate::{
     error::BDKCliError as Error,
     utils::{
         descriptors::{
-            format_descriptor_output, generate_descriptor_from_mnemonic,
-            generate_descriptor_with_mnemonic, generate_descriptors,
+            generate_descriptor_from_mnemonic, generate_descriptor_with_mnemonic,
+            generate_descriptors,
         },
         is_mnemonic,
+        output::FormatOutput,
     },
 };
 
 #[cfg(feature = "compiler")]
 use {
     crate::handlers::types::DescriptorResult,
-    crate::utils::output::FormatOutput,
     bdk_wallet::{
         bitcoin::XOnlyPublicKey,
         miniscript::{
@@ -48,7 +48,7 @@ pub fn handle_descriptor_command(
         // Generate new mnemonic and descriptors
         None => generate_descriptor_with_mnemonic(network, &desc_type),
     }?;
-    format_descriptor_output(&result, pretty)
+    result.format(pretty)
 }
 
 /// Handle the miniscript compiler sub-command
@@ -99,6 +99,7 @@ pub(crate) fn handle_compile_subcommand(
         public_descriptors: None,
         private_descriptors: None,
         mnemonic: None,
+        fingerprint: None,
     };
     result.format(pretty)
 }
index 51a1b56a9cc01820d00be1d80b35ce23e1585b36..dacd5783b303fce16e6ec76d43dc8a17889dfc10 100644 (file)
@@ -70,29 +70,28 @@ pub struct TransactionListResult(pub Vec<TransactionDetails>);
 
 impl FormatOutput for TransactionListResult {
     fn to_table(&self) -> Result<String, Error> {
-        let mut rows: Vec<Vec<CellStruct>> = vec![];
-
-        for tx in &self.0 {
-            rows.push(vec![
+        let rows = self.0.iter().map(|tx| {
+            vec![
                 tx.txid.clone().cell(),
                 tx.version_display.clone().cell().justify(Justify::Right),
                 tx.is_rbf.to_string().cell().justify(Justify::Center),
                 tx.input_count.to_string().cell().justify(Justify::Right),
                 tx.output_count.to_string().cell().justify(Justify::Right),
                 tx.total_value.to_string().cell().justify(Justify::Right),
-            ]);
-        }
+            ]
+        });
 
-        let title = vec![
-            "Txid".cell().bold(true),
-            "Version".cell().bold(true),
-            "Is RBF".cell().bold(true),
-            "Input Count".cell().bold(true),
-            "Output Count".cell().bold(true),
-            "Total Value (sat)".cell().bold(true),
-        ];
-
-        simple_table(rows, Some(title))
+        simple_table(
+            rows,
+            Some(vec![
+                "Txid".cell().bold(true),
+                "Version".cell().bold(true),
+                "Is RBF".cell().bold(true),
+                "Input Count".cell().bold(true),
+                "Output Count".cell().bold(true),
+                "Total Value (sat)".cell().bold(true),
+            ]),
+        )
     }
 }
 
@@ -400,8 +399,7 @@ impl FormatOutput for WalletsListResult {
             return Ok("No wallets configured yet.".to_string());
         }
 
-        let mut rows: Vec<Vec<CellStruct>> = vec![];
-        for (name, inner) in &self.0 {
+        let rows = self.0.iter().map(|(name, inner)| {
             let desc: String = inner.ext_descriptor.chars().take(30).collect();
             let desc_display = if inner.ext_descriptor.len() > 30 {
                 format!("{}...", desc)
@@ -409,12 +407,12 @@ impl FormatOutput for WalletsListResult {
                 desc
             };
 
-            rows.push(vec![
+            vec![
                 name.clone().cell(),
                 inner.network.clone().cell(),
                 desc_display.cell(),
-            ]);
-        }
+            ]
+        });
 
         simple_table(
             rows,
@@ -427,47 +425,68 @@ impl FormatOutput for WalletsListResult {
     }
 }
 
-
 #[derive(Serialize)]
 pub struct DescriptorResult {
     #[serde(skip_serializing_if = "Option::is_none")]
     pub descriptor: Option<String>,
-    
+
     #[serde(skip_serializing_if = "Option::is_none")]
     pub multipath_descriptor: Option<String>,
-    
+
     #[serde(skip_serializing_if = "Option::is_none")]
     pub public_descriptors: Option<KeychainPair<String>>,
-    
+
     #[serde(skip_serializing_if = "Option::is_none")]
     pub private_descriptors: Option<KeychainPair<String>>,
-    
+
     #[serde(skip_serializing_if = "Option::is_none")]
     pub mnemonic: Option<String>,
+
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub fingerprint: Option<String>,
 }
 
 impl FormatOutput for DescriptorResult {
     fn to_table(&self) -> Result<String, Error> {
         let mut rows: Vec<Vec<CellStruct>> = vec![];
-        
+
         if let Some(desc) = &self.descriptor {
             rows.push(vec!["Descriptor".cell().bold(true), desc.clone().cell()]);
         }
         if let Some(desc) = &self.multipath_descriptor {
-            rows.push(vec!["Multipath Descriptor".cell().bold(true), desc.clone().cell()]);
+            rows.push(vec![
+                "Multipath Descriptor".cell().bold(true),
+                desc.clone().cell(),
+            ]);
         }
         if let Some(pub_desc) = &self.public_descriptors {
-            rows.push(vec!["External Public".cell().bold(true), pub_desc.external.clone().cell()]);
-            rows.push(vec!["Internal Public".cell().bold(true), pub_desc.internal.clone().cell()]);
+            rows.push(vec![
+                "External Public".cell().bold(true),
+                pub_desc.external.clone().cell(),
+            ]);
+            rows.push(vec![
+                "Internal Public".cell().bold(true),
+                pub_desc.internal.clone().cell(),
+            ]);
         }
         if let Some(priv_desc) = &self.private_descriptors {
-            rows.push(vec!["External Private".cell().bold(true), priv_desc.external.clone().cell()]);
-            rows.push(vec!["Internal Private".cell().bold(true), priv_desc.internal.clone().cell()]);
+            rows.push(vec![
+                "External Private".cell().bold(true),
+                priv_desc.external.clone().cell(),
+            ]);
+            rows.push(vec![
+                "Internal Private".cell().bold(true),
+                priv_desc.internal.clone().cell(),
+            ]);
         }
         if let Some(mnemonic) = &self.mnemonic {
             rows.push(vec!["Mnemonic".cell().bold(true), mnemonic.clone().cell()]);
         }
 
+        if let Some(fp) = &self.fingerprint {
+            rows.push(vec!["Fingerprint".cell().bold(true), fp.clone().cell()]);
+        }
+
         simple_table(rows, None)
     }
 }
index 30a0f237b15b60a331d2fe57cb18f48ebb36793b..d228999367f56115f4007a619d03cebdccf02a16 100644 (file)
@@ -17,12 +17,15 @@ use bdk_wallet::{
     },
     template::DescriptorTemplate,
 };
-use cli_table::{Cell, CellStruct, Style, Table};
-use serde_json::{Value, json};
 
 use crate::error::BDKCliError as Error;
+use crate::handlers::types::{DescriptorResult, KeychainPair};
 
-pub fn generate_descriptors(desc_type: &str, key: &str, network: Network) -> Result<Value, Error> {
+pub fn generate_descriptors(
+    desc_type: &str,
+    key: &str,
+    network: Network,
+) -> Result<DescriptorResult, Error> {
     let is_private = key.starts_with("xprv") || key.starts_with("tprv");
 
     if is_private {
@@ -49,7 +52,7 @@ fn generate_private_descriptors(
     desc_type: &str,
     key: &str,
     network: Network,
-) -> Result<Value, Error> {
+) -> Result<DescriptorResult, Error> {
     use bdk_wallet::template::{Bip44, Bip49, Bip84, Bip86};
 
     let secp = Secp256k1::new();
@@ -85,17 +88,20 @@ fn generate_private_descriptors(
     let internal_priv = internal_desc.to_string_with_secret(&internal_keymap);
     let internal_pub = internal_desc.to_string();
 
-    Ok(json!({
-        "public_descriptors": {
-            "external": external_pub,
-            "internal": internal_pub
-        },
-        "private_descriptors": {
-            "external": external_priv,
-            "internal": internal_priv
-        },
-        "fingerprint": fingerprint.to_string()
-    }))
+    Ok(DescriptorResult {
+        descriptor: None,
+        multipath_descriptor: None,
+        public_descriptors: Some(KeychainPair {
+            external: external_pub,
+            internal: internal_pub,
+        }),
+        private_descriptors: Some(KeychainPair {
+            external: external_priv,
+            internal: internal_priv,
+        }),
+        mnemonic: None,
+        fingerprint: Some(fingerprint.to_string()),
+    })
 }
 
 /// Generate descriptors from public key (xpub/tpub)
@@ -103,7 +109,7 @@ pub fn generate_public_descriptors(
     desc_type: &str,
     key: &str,
     derivation_path: &DerivationPath,
-) -> Result<Value, Error> {
+) -> Result<DescriptorResult, Error> {
     let xpub: Xpub = key.parse()?;
     let fingerprint = xpub.fingerprint();
 
@@ -122,14 +128,17 @@ pub fn generate_public_descriptors(
 
     let external_pub = build_descriptor("0")?;
     let internal_pub = build_descriptor("1")?;
-
-    Ok(json!({
-        "public_descriptors": {
-            "external": external_pub,
-            "internal": internal_pub
-        },
-        "fingerprint": fingerprint.to_string()
-    }))
+    Ok(DescriptorResult {
+        descriptor: None,
+        multipath_descriptor: None,
+        public_descriptors: Some(KeychainPair {
+            external: external_pub,
+            internal: internal_pub,
+        }),
+        private_descriptors: None,
+        mnemonic: None,
+        fingerprint: Some(fingerprint.to_string()),
+    })
 }
 
 /// Build a descriptor from a public key
@@ -158,7 +167,7 @@ pub fn build_public_descriptor(
 pub fn generate_descriptor_with_mnemonic(
     network: Network,
     desc_type: &str,
-) -> Result<serde_json::Value, Error> {
+) -> Result<DescriptorResult, Error> {
     let mnemonic: GeneratedKey<Mnemonic, Segwitv0> =
         Mnemonic::generate((WordCount::Words12, Language::English)).map_err(Error::BIP39Error)?;
 
@@ -166,7 +175,7 @@ pub fn generate_descriptor_with_mnemonic(
     let xprv = Xpriv::new_master(network, &seed)?;
 
     let mut result = generate_descriptors(desc_type, &xprv.to_string(), network)?;
-    result["mnemonic"] = json!(mnemonic.to_string());
+    result.mnemonic = Some(mnemonic.to_string());
     Ok(result)
 }
 
@@ -175,91 +184,12 @@ pub fn generate_descriptor_from_mnemonic(
     mnemonic_str: &str,
     network: Network,
     desc_type: &str,
-) -> Result<serde_json::Value, Error> {
+) -> Result<DescriptorResult, Error> {
     let mnemonic = Mnemonic::parse_in(Language::English, mnemonic_str)?;
     let seed = mnemonic.to_seed("");
     let xprv = Xpriv::new_master(network, &seed)?;
 
     let mut result = generate_descriptors(desc_type, &xprv.to_string(), network)?;
-    result["mnemonic"] = json!(mnemonic_str);
+    result.mnemonic = Some(mnemonic_str.to_string());
     Ok(result)
 }
-
-pub fn format_descriptor_output(result: &Value, pretty: bool) -> Result<String, Error> {
-    if !pretty {
-        return Ok(serde_json::to_string_pretty(result)?);
-    }
-
-    let mut rows: Vec<Vec<CellStruct>> = vec![];
-
-    if let Some(desc_type) = result.get("type") {
-        rows.push(vec![
-            "Type".cell().bold(true),
-            desc_type.as_str().unwrap_or("N/A").cell(),
-        ]);
-    }
-
-    if let Some(finger_print) = result.get("fingerprint") {
-        rows.push(vec![
-            "Fingerprint".cell().bold(true),
-            finger_print.as_str().unwrap_or("N/A").cell(),
-        ]);
-    }
-
-    if let Some(network) = result.get("network") {
-        rows.push(vec![
-            "Network".cell().bold(true),
-            network.as_str().unwrap_or("N/A").cell(),
-        ]);
-    }
-    if let Some(multipath_desc) = result.get("multipath_descriptor") {
-        rows.push(vec![
-            "Multipart Descriptor".cell().bold(true),
-            multipath_desc.as_str().unwrap_or("N/A").cell(),
-        ]);
-    }
-    if let Some(pub_descs) = result.get("public_descriptors").and_then(|v| v.as_object()) {
-        if let Some(ext) = pub_descs.get("external") {
-            rows.push(vec![
-                "External Public".cell().bold(true),
-                ext.as_str().unwrap_or("N/A").cell(),
-            ]);
-        }
-        if let Some(int) = pub_descs.get("internal") {
-            rows.push(vec![
-                "Internal Public".cell().bold(true),
-                int.as_str().unwrap_or("N/A").cell(),
-            ]);
-        }
-    }
-    if let Some(priv_descs) = result
-        .get("private_descriptors")
-        .and_then(|v| v.as_object())
-    {
-        if let Some(ext) = priv_descs.get("external") {
-            rows.push(vec![
-                "External Private".cell().bold(true),
-                ext.as_str().unwrap_or("N/A").cell(),
-            ]);
-        }
-        if let Some(int) = priv_descs.get("internal") {
-            rows.push(vec![
-                "Internal Private".cell().bold(true),
-                int.as_str().unwrap_or("N/A").cell(),
-            ]);
-        }
-    }
-    if let Some(mnemonic) = result.get("mnemonic") {
-        rows.push(vec![
-            "Mnemonic".cell().bold(true),
-            mnemonic.as_str().unwrap_or("N/A").cell(),
-        ]);
-    }
-
-    let table = rows
-        .table()
-        .display()
-        .map_err(|e| Error::Generic(e.to_string()))?;
-
-    Ok(format!("{table}"))
-}
index 5c4360b8e3614e24a2e2eb853e7bb222918c2189..4ad6e8e25ea7fbcab612eea60db58ecf74cdc5eb 100644 (file)
@@ -19,10 +19,11 @@ pub trait FormatOutput: Serialize {
 }
 
 /// Helper for building simple tables
-pub fn simple_table(
-    rows: Vec<Vec<CellStruct>>,
-    title: Option<Vec<CellStruct>>,
-) -> Result<String, Error> {
+pub fn simple_table<R, C>(rows: R, title: Option<Vec<CellStruct>>) -> Result<String, Error>
+where
+    R: IntoIterator<Item = C>,
+    C: IntoIterator<Item = CellStruct>,
+{
     let mut table = rows.table();
     if let Some(title) = title {
         table = table.title(title);