Repository: rust-bitcoin/rust-bitcoin (language: rust)

Issue:
Abstract out "debug-print hex fields" somehow
"Seeing this and similar code in `Witness` makes me think it might be worth it to have `WrapDebug<F: Fn(&mut fmt::Formatter) -> fmt::Result>(F);` in `internals` and use that with an ad-hoc closure."

_Originally posted by @Kixunil in https://github.com/rust-bitcoin/rust-bitcoin/pull/4046#discussion_r1959155210_
            


Discussion / hints:
We have a couple things we want to do:

* In `Witness` we just want to loop over stuff and call `.as_hex` on each one. IMO this is pretty good.
* In `Hkdf` we want to apply a tagged hash and truncate it to get a "fingerprint" (and maaybe we want a `cfg` flag to expose the raw underlying data? I forget now what the other cfg flag we were talking about was. We should have a master issue about this alongside the MSRV one)
* In rust-secp `SecretKey` and `KeyPair` we want the same thing

cc #4070 which is closely related

Fix patch:
diff --git a/internals/src/lib.rs b/internals/src/lib.rs
index 87bba85f41..34fa682ae6 100644
--- a/internals/src/lib.rs
+++ b/internals/src/lib.rs
@@ -42,6 +42,7 @@ pub mod error;
 pub mod macros;
 mod parse;
 pub mod script;
+pub mod wrap_debug;
 #[cfg(feature = "serde")]
 #[macro_use]
 pub mod serde;
diff --git a/internals/src/wrap_debug.rs b/internals/src/wrap_debug.rs
new file mode 100644
index 0000000000..9a3f63d9b2
--- /dev/null
+++ b/internals/src/wrap_debug.rs
@@ -0,0 +1,9 @@
+//! Contains a wrapper for a function that implements `Debug`.
+use core::fmt;
+
+/// A wrapper for a function that implements `Debug`.
+pub struct WrapDebug<F: Fn(&mut fmt::Formatter) -> fmt::Result>(pub F);
+
+impl<F: Fn(&mut fmt::Formatter) -> fmt::Result> fmt::Debug for WrapDebug<F> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { (self.0)(f) }
+}
diff --git a/primitives/src/witness.rs b/primitives/src/witness.rs
index b802f9a1c4..e892334f1a 100644
--- a/primitives/src/witness.rs
+++ b/primitives/src/witness.rs
@@ -11,6 +11,7 @@ use core::ops::Index;
 use arbitrary::{Arbitrary, Unstructured};
 use hex::DisplayHex;
 use internals::compact_size;
+use internals::wrap_debug::WrapDebug;
 
 use crate::prelude::Vec;
 
@@ -232,15 +233,6 @@ fn decode_cursor(bytes: &[u8], start_of_indices: usize, index: usize) -> Option<
     }
 }
 
-/// Debug implementation that displays the witness as a structured output containing hex-encoded witness elements.
-struct DebugElements<'a>(&'a Witness);
-
-impl fmt::Debug for DebugElements<'_> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_list().entries(self.0.iter().map(|elem| elem.as_hex())).finish()
-    }
-}
-
 /// Debug implementation that displays the witness as a structured output containing:
 /// - Number of witness elements
 /// - Total bytes across all elements
@@ -252,7 +244,12 @@ impl fmt::Debug for Witness {
         f.debug_struct("Witness")
             .field("num_elements", &self.witness_elements)
             .field("total_bytes", &total_bytes)
-            .field("elements", &DebugElements(self))
+            .field(
+                "elements",
+                &WrapDebug(|f| {
+                    f.debug_list().entries(self.iter().map(|elem| elem.as_hex())).finish()
+                }),
+            )
             .finish()
     }
 }


Test patch: