Merge rust-bitcoin/rust-bitcoin#4624: impl LowerHex, UpperHex, Octal, and Binary for ChildNumber

7dc66e3476 impl LowerHex, UpperHex, Octal, and Binary for ChildNumber (vicjuma)

Pull request description:

  apoelstra Each trait forwards formatting to the inner u32 index. For Hardened variants, a suffix is appended based on whether alternate formatting is used per the discussion.

  See discussion: https://github.com/rust-bitcoin/rust-bitcoin/pull/4620#issuecomment-2974023604

ACKs for top commit:
  tcharding:
    ACK 7dc66e3476
  apoelstra:
    ACK 7dc66e3476c3ba08eb341a09f3c85a795b005159; successfully ran local tests

Tree-SHA512: 49e4acc82198d3a2c807bf8977413dd260a7081b83e8ad2c2b97d8fcaf85b78a54552098a5fe3020c78fe7b908d90a3bab86be20f330d3de569b5eba69e23348
This commit is contained in:
merge-script 2025-06-18 16:58:37 +00:00
commit c36d295836
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
1 changed files with 96 additions and 8 deletions

View File

@ -193,6 +193,29 @@ impl ChildNumber {
ChildNumber::Hardened { index: idx } => ChildNumber::from_hardened_idx(idx + 1),
}
}
/// Formats the child number using the provided formatting function.
///
/// For hardened child numbers appends a `'` or `hardened_alt_suffix`
/// depending on the formatter.
fn format_with<F>(
&self,
f: &mut fmt::Formatter,
format_fn: F,
hardened_alt_suffix: &str,
) -> fmt::Result
where
F: Fn(&u32, &mut fmt::Formatter) -> fmt::Result,
{
match *self {
ChildNumber::Hardened { index } => {
format_fn(&index, f)?;
let alt = f.alternate();
f.write_str(if alt { hardened_alt_suffix } else { "'" })
}
ChildNumber::Normal { index } => format_fn(&index, f),
}
}
}
impl From<u32> for ChildNumber {
@ -216,14 +239,31 @@ impl From<ChildNumber> for u32 {
impl fmt::Display for ChildNumber {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ChildNumber::Hardened { index } => {
fmt::Display::fmt(&index, f)?;
let alt = f.alternate();
f.write_str(if alt { "h" } else { "'" })
self.format_with(f, fmt::Display::fmt, "h")
}
ChildNumber::Normal { index } => fmt::Display::fmt(&index, f),
}
impl fmt::LowerHex for ChildNumber {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.format_with(f, fmt::LowerHex::fmt, "h")
}
}
impl fmt::UpperHex for ChildNumber {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.format_with(f, fmt::UpperHex::fmt, "H")
}
}
impl fmt::Octal for ChildNumber {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.format_with(f, fmt::Octal::fmt, "h")
}
}
impl fmt::Binary for ChildNumber {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.format_with(f, fmt::Binary::fmt, "h")
}
}
@ -1123,6 +1163,54 @@ mod tests {
assert_eq!(format!("{:#}", path), "84h/0h/0h/0/0");
}
#[test]
fn test_lowerhex_formatting() {
let normal = Normal { index: 42 };
let hardened = Hardened { index: 42 };
assert_eq!(format!("{:x}", normal), "2a");
assert_eq!(format!("{:#x}", normal), "0x2a");
assert_eq!(format!("{:x}", hardened), "2a'");
assert_eq!(format!("{:#x}", hardened), "0x2ah");
}
#[test]
fn test_upperhex_formatting() {
let normal = Normal { index: 42 };
let hardened = Hardened { index: 42 };
assert_eq!(format!("{:X}", normal), "2A");
assert_eq!(format!("{:#X}", normal), "0x2A");
assert_eq!(format!("{:X}", hardened), "2A'");
assert_eq!(format!("{:#X}", hardened), "0x2AH");
}
#[test]
fn test_octal_formatting() {
let normal = Normal { index: 42 };
let hardened = Hardened { index: 42 };
assert_eq!(format!("{:o}", normal), "52");
assert_eq!(format!("{:#o}", normal), "0o52");
assert_eq!(format!("{:o}", hardened), "52'");
assert_eq!(format!("{:#o}", hardened), "0o52h");
}
#[test]
fn test_binary_formatting() {
let normal = Normal { index: 42 };
let hardened = Hardened { index: 42 };
assert_eq!(format!("{:b}", normal), "101010");
assert_eq!(format!("{:#b}", normal), "0b101010");
assert_eq!(format!("{:b}", hardened), "101010'");
assert_eq!(format!("{:#b}", hardened), "0b101010h");
}
#[test]
fn parse_derivation_path_out_of_range() {
let invalid_path = "2147483648";