diff --git a/bitcoin/src/address/error.rs b/bitcoin/src/address/error.rs index b1757da6..be0a573d 100644 --- a/bitcoin/src/address/error.rs +++ b/bitcoin/src/address/error.rs @@ -9,26 +9,6 @@ use crate::blockdata::script::{witness_program, witness_version}; use crate::prelude::*; use crate::Network; -/// Address's network differs from required one. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct NetworkValidationError { - /// Network that was required. - pub(crate) required: Network, - /// The address itself. - pub(crate) address: Address, -} - -impl fmt::Display for NetworkValidationError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "address ")?; - fmt::Display::fmt(&self.address.0, f)?; - write!(f, " is not valid on {}", self.required) - } -} - -#[cfg(feature = "std")] -impl std::error::Error for NetworkValidationError {} - /// Error while generating address from script. #[derive(Debug, Clone, PartialEq, Eq)] #[non_exhaustive] @@ -143,6 +123,8 @@ pub enum ParseError { InvalidBase58PayloadLength(InvalidBase58PayloadLengthError), /// Invalid legacy address prefix in base58 data payload. InvalidLegacyPrefix(InvalidLegacyPrefixError), + /// Address's network differs from required one. + NetworkValidation(NetworkValidationError), } internals::impl_from_infallible!(ParseError); @@ -160,6 +142,7 @@ impl fmt::Display for ParseError { LegacyAddressTooLong(ref e) => write_err!(f, "legacy address base58 string"; e), InvalidBase58PayloadLength(ref e) => write_err!(f, "legacy address base58 data"; e), InvalidLegacyPrefix(ref e) => write_err!(f, "legacy address base58 prefix"; e), + NetworkValidation(ref e) => write_err!(f, "validation error"; e), } } } @@ -178,6 +161,7 @@ impl std::error::Error for ParseError { LegacyAddressTooLong(ref e) => Some(e), InvalidBase58PayloadLength(ref e) => Some(e), InvalidLegacyPrefix(ref e) => Some(e), + NetworkValidation(ref e) => Some(e), } } } @@ -214,6 +198,10 @@ impl From for ParseError { fn from(e: InvalidLegacyPrefixError) -> Self { Self::InvalidLegacyPrefix(e) } } +impl From for ParseError { + fn from(e: NetworkValidationError) -> Self { Self::NetworkValidation(e) } +} + /// Unknown HRP error. #[derive(Debug, Clone, PartialEq, Eq)] #[non_exhaustive] @@ -228,6 +216,26 @@ impl std::error::Error for UnknownHrpError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None } } +/// Address's network differs from required one. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct NetworkValidationError { + /// Network that was required. + pub(crate) required: Network, + /// The address itself. + pub(crate) address: Address, +} + +impl fmt::Display for NetworkValidationError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "address ")?; + fmt::Display::fmt(&self.address.0, f)?; + write!(f, " is not valid on {}", self.required) + } +} + +#[cfg(feature = "std")] +impl std::error::Error for NetworkValidationError {} + /// Decoded base58 data was an invalid length. #[derive(Debug, Clone, PartialEq, Eq)] pub struct InvalidBase58PayloadLengthError { diff --git a/bitcoin/src/address/mod.rs b/bitcoin/src/address/mod.rs index df1c7906..1af1580c 100644 --- a/bitcoin/src/address/mod.rs +++ b/bitcoin/src/address/mod.rs @@ -667,12 +667,50 @@ impl Address { /// /// For details about this mechanism, see section [*Parsing addresses*](Address#parsing-addresses) /// on [`Address`]. + /// + /// # Errors + /// + /// This function only ever returns the [`ParseError::NetworkValidation`] variant of + /// `ParseError`. This is not how we normally implement errors in this library but + /// `require_network` is not a typical function, it is conceptually part of string parsing. + /// + /// # Examples + /// + /// ``` + /// use bitcoin::address::{NetworkChecked, NetworkUnchecked, ParseError}; + /// use bitcoin::{Address, Network}; + /// + /// const ADDR: &str = "bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs"; + /// + /// fn parse_and_validate_address(network: Network) -> Result { + /// let address = ADDR.parse::>()? + /// .require_network(network)?; + /// Ok(address) + /// } + /// + /// fn parse_and_validate_address_combinator(network: Network) -> Result { + /// let address = ADDR.parse::>() + /// .and_then(|a| a.require_network(network))?; + /// Ok(address) + /// } + /// + /// fn parse_and_validate_address_show_types(network: Network) -> Result { + /// let address: Address = ADDR.parse::>()? + /// .require_network(network)?; + /// Ok(address) + /// } + /// + /// let network = Network::Bitcoin; // Don't hard code network in applications. + /// let _ = parse_and_validate_address(network).unwrap(); + /// let _ = parse_and_validate_address_combinator(network).unwrap(); + /// let _ = parse_and_validate_address_show_types(network).unwrap(); + /// ``` #[inline] - pub fn require_network(self, required: Network) -> Result { + pub fn require_network(self, required: Network) -> Result { if self.is_valid_for_network(required) { Ok(self.assume_checked()) } else { - Err(NetworkValidationError { required, address: self }) + Err(NetworkValidationError { required, address: self }.into()) } }