Merge rust-bitcoin/rust-bitcoin#2516: Impl `From<core::convert::Infallible>` for Errors

b9f7462958 Implement infallible for errors (Liam Aharon)

Pull request description:

  Closes https://github.com/rust-bitcoin/rust-bitcoin/issues/1222

  Example usage
  ```rust
  use internals::impl_from_infallible;

  #[derive(Debug, Clone, PartialEq, Eq)]
  enum Error {
      SomeError(u8),
      SomeOtherError(u32, u32),
  }

  impl_from_infallible!(Error);
  ```

  Feel free to request changes :)

ACKs for top commit:
  apoelstra:
    ACK b9f7462958
  Kixunil:
    ACK b9f7462958

Tree-SHA512: 33576248c6c7f26f59c24827e444ccefc4a299365416f5e396bb441318c0d2f17f4d5f3bad279d201b6b358147bc977e88d6fedd02a98fe538d3192483e75bcd
This commit is contained in:
Andrew Poelstra 2024-03-08 23:50:57 +00:00
commit db01a6363f
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
31 changed files with 165 additions and 0 deletions

View File

@ -290,6 +290,7 @@ More specifically an error should
- derive `Debug, Clone, PartialEq, Eq` (and `Copy` iff not `non_exhaustive`).
- implement Display using `write_err!()` macro if a variant contains an inner error source.
- have `Error` suffix
- call `internals::impl_from_infallible!
- implement `std::error::Error` if they are public (feature gated on "std").
```rust
@ -302,6 +303,9 @@ pub enum Error {
/// Documentation for variant B.
B,
}
internals::impl_from_infallible!(Error);
```

View File

@ -12,6 +12,7 @@ checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
name = "base58check"
version = "0.1.0"
dependencies = [
"bitcoin-internals",
"bitcoin_hashes",
"hex-conservative",
]

View File

@ -12,6 +12,7 @@ checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
name = "base58check"
version = "0.1.0"
dependencies = [
"bitcoin-internals",
"bitcoin_hashes",
"hex-conservative",
]

View File

@ -22,6 +22,7 @@ rustdoc-args = ["--cfg", "docsrs"]
[dependencies]
hashes = { package = "bitcoin_hashes", version = "0.13.0", default-features = false, features = ["alloc"] }
internals = { package = "bitcoin-internals", version = "0.2.0" }
[dev-dependencies]
hex = { package = "hex-conservative", version = "0.1.1", default-features = false, features = ["alloc"] }

View File

@ -228,6 +228,8 @@ pub enum Error {
TooShort(usize),
}
internals::impl_from_infallible!(Error);
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Error::*;

View File

@ -41,6 +41,8 @@ pub enum FromScriptError {
WitnessVersion(witness_version::TryFromError),
}
internals::impl_from_infallible!(FromScriptError);
impl fmt::Display for FromScriptError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use FromScriptError::*;
@ -82,6 +84,8 @@ pub enum P2shError {
ExcessiveScriptSize,
}
internals::impl_from_infallible!(P2shError);
impl fmt::Display for P2shError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use P2shError::*;
@ -135,6 +139,8 @@ pub enum ParseError {
UnknownHrp(UnknownHrpError),
}
internals::impl_from_infallible!(ParseError);
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use ParseError::*;

View File

@ -28,6 +28,8 @@ pub enum Error {
InvalidPrefill,
}
internals::impl_from_infallible!(Error);
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {

View File

@ -77,6 +77,8 @@ pub enum Error {
Io(io::Error),
}
internals::impl_from_infallible!(Error);
impl Display for Error {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
use Error::*;

View File

@ -491,6 +491,8 @@ pub enum Error {
InvalidPublicKeyHexLength(usize),
}
internals::impl_from_infallible!(Error);
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Error::*;

View File

@ -420,6 +420,8 @@ pub enum Bip34Error {
NegativeHeight,
}
internals::impl_from_infallible!(Bip34Error);
impl fmt::Display for Bip34Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Bip34Error::*;
@ -456,6 +458,8 @@ pub enum ValidationError {
BadTarget,
}
internals::impl_from_infallible!(ValidationError);
impl fmt::Display for ValidationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use ValidationError::*;

View File

@ -626,6 +626,8 @@ pub enum Error {
Parse(ParseIntError),
}
internals::impl_from_infallible!(Error);
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Error::*;
@ -723,6 +725,8 @@ pub enum OperationError {
InvalidComparison,
}
internals::impl_from_infallible!(OperationError);
impl fmt::Display for OperationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use OperationError::*;
@ -754,6 +758,8 @@ enum ParseError {
Conversion(i64),
}
internals::impl_from_infallible!(ParseError);
impl ParseError {
fn invalid_int<S: Into<String>>(s: S) -> impl FnOnce(core::num::ParseIntError) -> Self {
move |source| Self::InvalidInteger { source, input: s.into() }

View File

@ -282,6 +282,8 @@ pub enum Error {
IncompatibleTime(LockTime, Time),
}
internals::impl_from_infallible!(Error);
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Error::*;

View File

@ -699,6 +699,8 @@ pub enum Error {
Serialization,
}
internals::impl_from_infallible!(Error);
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Error::*;
@ -737,6 +739,8 @@ enum UintError {
NumericOverflow,
}
internals::impl_from_infallible!(UintError);
impl From<UintError> for Error {
fn from(error: UintError) -> Self {
match error {

View File

@ -135,6 +135,8 @@ pub enum Error {
InvalidSegwitV0Length(usize),
}
internals::impl_from_infallible!(Error);
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Error::*;

View File

@ -175,6 +175,8 @@ pub enum FromStrError {
Invalid(TryFromError),
}
internals::impl_from_infallible!(FromStrError);
impl fmt::Display for FromStrError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use FromStrError::*;
@ -216,6 +218,8 @@ pub enum TryFromInstructionError {
DataPush,
}
internals::impl_from_infallible!(TryFromInstructionError);
impl fmt::Display for TryFromInstructionError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use TryFromInstructionError::*;

View File

@ -130,6 +130,8 @@ pub enum ParseOutPointError {
VoutNotCanonical,
}
internals::impl_from_infallible!(ParseOutPointError);
impl fmt::Display for ParseOutPointError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use ParseOutPointError::*;

View File

@ -61,6 +61,8 @@ pub enum Error {
UnsupportedSegwitFlag(u8),
}
internals::impl_from_infallible!(Error);
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Error::*;

View File

@ -368,6 +368,8 @@ enum DecodeError<E> {
Other(E),
}
internals::impl_from_infallible!(DecodeError<E>);
// not a trait impl because we panic on some variants
fn consensus_error_into_serde<E: serde::de::Error>(error: ConsensusError) -> E {
match error {

View File

@ -209,6 +209,8 @@ pub enum TxVerifyError {
UnknownSpentOutput(OutPoint),
}
internals::impl_from_infallible!(TxVerifyError);
impl fmt::Display for TxVerifyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use TxVerifyError::*;

View File

@ -213,6 +213,8 @@ pub enum Error {
Secp256k1(secp256k1::Error),
}
internals::impl_from_infallible!(Error);
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Error::*;

View File

@ -896,6 +896,8 @@ pub enum FromSliceError {
InvalidLength(usize),
}
internals::impl_from_infallible!(FromSliceError);
impl fmt::Display for FromSliceError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use FromSliceError::*;
@ -934,6 +936,8 @@ pub enum FromWifError {
Secp256k1(secp256k1::Error),
}
internals::impl_from_infallible!(FromWifError);
impl fmt::Display for FromWifError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use FromWifError::*;
@ -974,6 +978,8 @@ pub enum ParsePublicKeyError {
InvalidHexLength(usize),
}
internals::impl_from_infallible!(ParsePublicKeyError);
impl fmt::Display for ParsePublicKeyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use ParsePublicKeyError::*;
@ -1010,6 +1016,8 @@ pub enum ParseCompressedPublicKeyError {
Hex(hex::HexToArrayError),
}
internals::impl_from_infallible!(ParseCompressedPublicKeyError);
impl fmt::Display for ParseCompressedPublicKeyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use ParseCompressedPublicKeyError::*;

View File

@ -275,6 +275,8 @@ pub enum PrevoutsIndexError {
InvalidAllIndex,
}
internals::impl_from_infallible!(PrevoutsIndexError);
impl fmt::Display for PrevoutsIndexError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use PrevoutsIndexError::*;
@ -1151,6 +1153,8 @@ pub enum TaprootError {
InvalidSighashType(u32),
}
internals::impl_from_infallible!(TaprootError);
impl fmt::Display for TaprootError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use TaprootError::*;
@ -1208,6 +1212,8 @@ pub enum P2wpkhError {
NotP2wpkhScript,
}
internals::impl_from_infallible!(P2wpkhError);
impl From<transaction::InputsIndexError> for P2wpkhError {
fn from(value: transaction::InputsIndexError) -> Self {
P2wpkhError::Sighash(value)
@ -1273,6 +1279,8 @@ pub enum AnnexError {
IncorrectPrefix(u8),
}
internals::impl_from_infallible!(AnnexError);
impl fmt::Display for AnnexError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use AnnexError::*;
@ -1379,6 +1387,8 @@ pub enum SigningDataError<E> {
Sighash(E),
}
internals::impl_from_infallible!(SigningDataError<E>);
impl<E> SigningDataError<E> {
/// Returns the sighash variant, panicking if it's IO.
///

View File

@ -97,6 +97,8 @@ pub enum SigFromSliceError {
InvalidSignatureSize(usize),
}
internals::impl_from_infallible!(SigFromSliceError);
impl fmt::Display for SigFromSliceError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use SigFromSliceError::*;

View File

@ -498,6 +498,8 @@ pub enum MerkleBlockError {
IdenticalHashesFound,
}
internals::impl_from_infallible!(MerkleBlockError);
impl fmt::Display for MerkleBlockError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use MerkleBlockError::*;

View File

@ -103,6 +103,8 @@ pub enum Error {
Io(io::Error),
}
internals::impl_from_infallible!(Error);
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Error::*;

View File

@ -675,6 +675,8 @@ pub enum GetKeyError {
NotSupported,
}
internals::impl_from_infallible!(GetKeyError);
impl fmt::Display for GetKeyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use GetKeyError::*;
@ -784,6 +786,8 @@ pub enum SignError {
Unsupported,
}
internals::impl_from_infallible!(SignError);
impl fmt::Display for SignError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use SignError::*;
@ -865,6 +869,8 @@ pub enum ExtractTxError {
},
}
internals::impl_from_infallible!(ExtractTxError);
impl fmt::Display for ExtractTxError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ExtractTxError::*;
@ -915,6 +921,8 @@ pub enum IndexOutOfBoundsError {
},
}
internals::impl_from_infallible!(IndexOutOfBoundsError);
impl fmt::Display for IndexOutOfBoundsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use IndexOutOfBoundsError::*;
@ -966,6 +974,8 @@ mod display_from_str {
Base64Encoding(::base64::DecodeError),
}
internals::impl_from_infallible!(PsbtParseError);
impl Display for PsbtParseError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
use self::PsbtParseError::*;

View File

@ -43,6 +43,8 @@ mod message_signing {
UnsupportedAddressType(AddressType),
}
internals::impl_from_infallible!(MessageSignatureError);
impl fmt::Display for MessageSignatureError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use MessageSignatureError::*;

View File

@ -587,6 +587,8 @@ pub enum IncompleteBuilderError {
HiddenParts(TaprootBuilder),
}
internals::impl_from_infallible!(IncompleteBuilderError);
impl IncompleteBuilderError {
/// Converts error into the original incomplete [`TaprootBuilder`] instance.
pub fn into_builder(self) -> TaprootBuilder {
@ -631,6 +633,8 @@ pub enum HiddenNodesError {
HiddenParts(NodeInfo),
}
internals::impl_from_infallible!(HiddenNodesError);
impl HiddenNodesError {
/// Converts error into the original incomplete [`NodeInfo`] instance.
pub fn into_node_info(self) -> NodeInfo {
@ -1324,6 +1328,8 @@ pub enum TaprootBuilderError {
EmptyTree,
}
internals::impl_from_infallible!(TaprootBuilderError);
impl fmt::Display for TaprootBuilderError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use TaprootBuilderError::*;
@ -1384,6 +1390,8 @@ pub enum TaprootError {
EmptyTree,
}
internals::impl_from_infallible!(TaprootError);
impl fmt::Display for TaprootError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use TaprootError::*;

View File

@ -113,3 +113,59 @@ macro_rules! const_assert {
const _: [(); 0 - !$x as usize] = [];
}};
}
/// Derives `From<core::convert::Infallible>` for the given type.
///
/// Supports types with arbitrary combinations of lifetimes and type parameters.
///
/// Note: Paths are not supported (for ex. impl_from_infallible!(Hello<D: std::fmt::Display>).
///
/// ## Examples
///
/// ```rust
/// # use core::fmt::{Display, Debug};
/// use bitcoin_internals::impl_from_infallible;
///
/// enum AlphaEnum { Item }
/// impl_from_infallible!(AlphaEnum);
///
/// enum BetaEnum<'b> { Item(&'b usize) }
/// impl_from_infallible!(BetaEnum<'b>);
///
/// enum GammaEnum<T> { Item(T) };
/// impl_from_infallible!(GammaEnum<T>);
///
/// enum DeltaEnum<'b, 'a: 'static + 'b, T: 'a, D: Debug + Display + 'a> {
/// Item((&'b usize, &'a usize, T, D))
/// }
/// impl_from_infallible!(DeltaEnum<'b, 'a: 'static + 'b, T: 'a, D: Debug + Display + 'a>);
///
/// struct AlphaStruct;
/// impl_from_infallible!(AlphaStruct);
///
/// struct BetaStruct<'b>(&'b usize);
/// impl_from_infallible!(BetaStruct<'b>);
///
/// struct GammaStruct<T>(T);
/// impl_from_infallible!(GammaStruct<T>);
///
/// struct DeltaStruct<'b, 'a: 'static + 'b, T: 'a, D: Debug + Display + 'a> {
/// hello: &'a T,
/// what: &'b D,
/// }
/// impl_from_infallible!(DeltaStruct<'b, 'a: 'static + 'b, T: 'a, D: Debug + Display + 'a>);
/// ```
///
/// See <https://stackoverflow.com/a/61189128> for more information about this macro.
#[macro_export]
macro_rules! impl_from_infallible {
( $name:ident $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? ) => {
impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?
From<core::convert::Infallible>
for $name
$(< $( $lt ),+ >)?
{
fn from(never: core::convert::Infallible) -> Self { match never {} }
}
}
}

View File

@ -116,6 +116,10 @@ macro_rules! define_errorkind {
),*
}
impl From<core::convert::Infallible> for ErrorKind {
fn from(never: core::convert::Infallible) -> Self { match never {} }
}
impl ErrorKind {
fn description(&self) -> &'static str {
match self {

View File

@ -161,6 +161,8 @@ pub enum ParseError {
MissingDenomination(MissingDenominationError),
}
internals::impl_from_infallible!(ParseError);
impl From<ParseAmountError> for ParseError {
fn from(e: ParseAmountError) -> Self { Self::Amount(e) }
}
@ -255,6 +257,8 @@ impl From<InvalidCharacterError> for ParseAmountError {
}
}
internals::impl_from_infallible!(ParseAmountError);
impl fmt::Display for ParseAmountError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use ParseAmountError::*;
@ -447,6 +451,8 @@ pub enum ParseDenominationError {
PossiblyConfusing(PossiblyConfusingDenominationError),
}
internals::impl_from_infallible!(ParseDenominationError);
impl fmt::Display for ParseDenominationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use ParseDenominationError::*;
@ -621,6 +627,8 @@ enum InnerParseError {
InvalidCharacter(InvalidCharacterError),
}
internals::impl_from_infallible!(InnerParseError);
impl InnerParseError {
fn convert(self, is_signed: bool) -> ParseAmountError {
match self {