Merge rust-bitcoin/rust-bitcoin#1578: Implement `Debug` for generic `Address<V: NetworkValidation>`

ebfbe74243 Implement `Debug` for generic `Address<V: NetworkValidation>` (Jiri Jakes)

Pull request description:

  Previously `Debug` was implemented for both `Address<NetworkChecked>` and `Address<NetworkUnchecked>`, but not for cases when the `NetworkValidation` parameter was generic. This change adds this ability. Based on Kixunil's tip.

  With previous implementation, the `test_address_debug()` resulted in error:

  ![image](https://user-images.githubusercontent.com/1381856/213907042-f1b27f41-fa46-4fa0-b816-cc4df53f5d29.png)

  The added `Debug` on `NetworkChecked` and `NetworkUnchecked` are required by compiler.

  ---

  While dealing with derives and impls, I also attempted to turn all the derives on `Address` into manual impls (see Kixunil's suggestion in https://github.com/rust-bitcoin/rust-bitcoin/pull/1489#discussion_r1052448057). The motivation behind this was the possibility to remove derives on `NetworkChecked` and `NetworkUnchecked`, too. However, even with manual impls, all the traits on `NetworkChecked` and `NetworkUnchecked` were still required by compiler in this sort of situations (see also the rest of the same discussion linked above). I do not fully understand why, perhaps limitation of this way of sealing traits?

  It can be demonstrated by removing `Debug` derivation on `NetworkUnchecked` and `NetworkChecked` in this PR and running `test_address_debug()`.

  Therefore, if we want to allow users of the library to define types generic in `NetworkValidation` and at the same time derive impls, it seems to me that `NetworkChecked` and `NetworkUnchecked` will have to have the same set of impls as `Address` itself.

ACKs for top commit:
  Kixunil:
    ACK ebfbe74243
  tcharding:
    ACK ebfbe74243
  apoelstra:
    ACK ebfbe74243

Tree-SHA512: 87f3fa4539602f31bf4513a29543b04e943c3899d8ece36d0d905c3b5a2d76e29eb86242694b5c494faa5e54bb8f69f5048849916c6438ddd35030368f710353
This commit is contained in:
Andrew Poelstra 2023-01-23 14:36:42 +00:00
commit 4b02d90ce4
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
1 changed files with 36 additions and 14 deletions

View File

@ -571,20 +571,23 @@ mod sealed {
/// Marker of status of address's network validation. See section [*Parsing addresses*](Address#parsing-addresses)
/// on [`Address`] for details.
pub trait NetworkValidation: sealed::NetworkValidation {}
pub trait NetworkValidation: sealed::NetworkValidation {
/// Indicates whether this `NetworkValidation` is `NetworkChecked` or not.
const IS_CHECKED: bool;
}
/// Marker that address's network has been successfully validated. See section [*Parsing addresses*](Address#parsing-addresses)
/// on [`Address`] for details.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum NetworkChecked {}
/// Marker that address's network has not yet been validated. See section [*Parsing addresses*](Address#parsing-addresses)
/// on [`Address`] for details.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum NetworkUnchecked {}
impl NetworkValidation for NetworkChecked {}
impl NetworkValidation for NetworkUnchecked {}
impl NetworkValidation for NetworkChecked { const IS_CHECKED: bool = true; }
impl NetworkValidation for NetworkUnchecked { const IS_CHECKED: bool = false; }
/// A Bitcoin address.
///
@ -970,15 +973,15 @@ impl fmt::Display for Address {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { self.fmt_internal(fmt) }
}
impl fmt::Debug for Address {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.fmt_internal(f) }
}
impl fmt::Debug for Address<NetworkUnchecked> {
impl<V: NetworkValidation> fmt::Debug for Address<V> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Address<NetworkUnchecked>(")?;
self.fmt_internal(f)?;
write!(f, ")")
if V::IS_CHECKED {
self.fmt_internal(f)
} else {
write!(f, "Address<NetworkUnchecked>(")?;
self.fmt_internal(f)?;
write!(f, ")")
}
}
}
@ -1005,7 +1008,7 @@ fn find_bech32_prefix(bech32: &str) -> &str {
}
}
// Address can be parsed only with NetworkUnchecked.
/// Address can be parsed only with `NetworkUnchecked`.
impl FromStr for Address<NetworkUnchecked> {
type Err = Error;
@ -1232,6 +1235,25 @@ mod tests {
roundtrips(&addr);
}
#[test]
fn test_address_debug() {
// This is not really testing output of Debug but the ability and proper functioning
// of Debug derivation on structs generic in NetworkValidation.
#[derive(Debug)] #[allow(unused)]
struct Test<V: NetworkValidation> { address: Address<V> }
let addr_str = "33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k";
let unchecked = Address::from_str(addr_str).unwrap();
assert_eq!(
format!("{:?}", Test { address: unchecked.clone() }),
format!("Test {{ address: Address<NetworkUnchecked>({}) }}", addr_str));
assert_eq!(
format!("{:?}", Test { address: unchecked.assume_checked() }),
format!("Test {{ address: {} }}", addr_str));
}
#[test]
fn test_address_type() {
let addresses = [