Merge rust-bitcoin/rust-bitcoin#1291: Remove `user_enum` macro

f429c22599 Remove user_enum macro (Tobin C. Harding)
ad29084582 Add Network serde roundtrip test (Tobin C. Harding)

Pull request description:

  Remove the `user_enum` macro because it is only used once and is unnecessarily complicated.

  Patch 1: Add a preparatory unit test to make sure patch 2 maintains the current serde logic.
  Patch 2: Simplify `Network` by removing `user_enum`

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

Tree-SHA512: 770ee0aa82be711ed11bdfda338de8d13071743cdc8d7e3e1252a6759dfdfa2be6ff1d8cd286acef75dc5a5d73a6bc5f565b02cb20d4b549e0714cef5d7c1973
This commit is contained in:
Andrew Poelstra 2022-09-20 14:48:13 +00:00
commit b309f41076
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
2 changed files with 74 additions and 102 deletions

View File

@ -538,94 +538,6 @@ macro_rules! impl_bytes_newtype {
} }
pub(crate) use impl_bytes_newtype; pub(crate) use impl_bytes_newtype;
macro_rules! user_enum {
(
$(#[$attr:meta])*
pub enum $name:ident {
$(#[$doc:meta]
$elem:ident <-> $txt:literal),*
}
) => (
$(#[$attr])*
pub enum $name {
$(#[$doc] $elem),*
}
impl core::fmt::Display for $name {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.pad(match *self {
$($name::$elem => $txt),*
})
}
}
impl core::str::FromStr for $name {
type Err = $crate::io::Error;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
$($txt => Ok($name::$elem)),*,
_ => {
#[cfg(not(feature = "std"))] let message = "Unknown network";
#[cfg(feature = "std")] let message = format!("Unknown network (type {})", s);
Err($crate::io::Error::new(
$crate::io::ErrorKind::InvalidInput,
message,
))
}
}
}
}
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl<'de> $crate::serde::Deserialize<'de> for $name {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: $crate::serde::Deserializer<'de>,
{
use core::fmt::{self, Formatter};
struct Visitor;
impl<'de> $crate::serde::de::Visitor<'de> for Visitor {
type Value = $name;
fn expecting(&self, f: &mut Formatter) -> fmt::Result {
f.write_str("an enum value")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: $crate::serde::de::Error,
{
static FIELDS: &'static [&'static str] = &[$(stringify!($txt)),*];
$( if v == $txt { Ok($name::$elem) } )else*
else {
Err(E::unknown_variant(v, FIELDS))
}
}
}
deserializer.deserialize_str(Visitor)
}
}
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl $crate::serde::Serialize for $name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: $crate::serde::Serializer,
{
serializer.collect_str(&self)
}
}
);
}
pub(crate) use user_enum;
/// Asserts a boolean expression at compile time. /// Asserts a boolean expression at compile time.
macro_rules! const_assert { macro_rules! const_assert {
($x:expr) => {{ ($x:expr) => {{

View File

@ -27,10 +27,13 @@
//! ``` //! ```
use core::{fmt, ops, convert::From}; use core::{fmt, ops, convert::From};
use core::str::FromStr;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::io; use crate::io;
use crate::consensus::encode::{self, Encodable, Decodable}; use crate::consensus::encode::{self, Encodable, Decodable};
use crate::internal_macros::user_enum;
/// Version of the protocol as appearing in network message headers /// Version of the protocol as appearing in network message headers
/// This constant is used to signal to other peers which features you support. /// This constant is used to signal to other peers which features you support.
@ -49,19 +52,21 @@ use crate::internal_macros::user_enum;
/// 60001 - Support `pong` message and nonce in `ping` message /// 60001 - Support `pong` message and nonce in `ping` message
pub const PROTOCOL_VERSION: u32 = 70001; pub const PROTOCOL_VERSION: u32 = 70001;
user_enum! { /// The cryptocurrency network to act on.
/// The cryptocurrency to act on #[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Debug)]
#[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Network { #[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
/// Classic Bitcoin #[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
Bitcoin <-> "bitcoin", #[non_exhaustive]
/// Bitcoin's testnet pub enum Network {
Testnet <-> "testnet", /// Mainnet Bitcoin.
/// Bitcoin's signet Bitcoin,
Signet <-> "signet", /// Bitcoin's testnet network.
/// Bitcoin's regtest Testnet,
Regtest <-> "regtest" /// Bitcoin's signet network.
} Signet,
/// Bitcoin's regtest network.
Regtest,
} }
impl Network { impl Network {
@ -108,6 +113,43 @@ impl Network {
} }
} }
impl fmt::Display for Network {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
use Network::*;
let s = match *self {
Bitcoin => "bitcoin",
Testnet => "testnet",
Signet => "signet",
Regtest => "regtest",
};
write!(f, "{}", s)
}
}
impl FromStr for Network {
type Err = io::Error;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
use Network::*;
let network = match s {
"bitcoin" => Bitcoin,
"testnet" => Testnet,
"signet" => Signet,
"regtest" => Regtest,
_ => {
#[cfg(feature = "std")]
let message = format!("Unknown network (type {})", s);
#[cfg(not(feature = "std"))]
let message = "Unknown network";
return Err(io::Error::new(io::ErrorKind::InvalidInput, message));
}
};
Ok(network)
}
}
/// Flags to indicate which network services a node supports. /// Flags to indicate which network services a node supports.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ServiceFlags(u64); pub struct ServiceFlags(u64);
@ -348,4 +390,22 @@ mod tests {
let flag = ServiceFlags::WITNESS | 0xf0.into(); let flag = ServiceFlags::WITNESS | 0xf0.into();
assert_eq!("ServiceFlags(WITNESS|COMPACT_FILTERS|0xb0)", flag.to_string()); assert_eq!("ServiceFlags(WITNESS|COMPACT_FILTERS|0xb0)", flag.to_string());
} }
#[test]
#[cfg(feature = "serde")]
fn serde_roundtrip() {
use Network::*;
let tests = vec![(Bitcoin, "bitcoin"), (Testnet, "testnet"), (Signet, "signet"), (Regtest, "regtest")];
for tc in tests {
let network = tc.0;
let want = format!("\"{}\"", tc.1);
let got = serde_json::to_string(&tc.0).expect("failed to serialize network");
assert_eq!(got, want);
let back: Network = serde_json::from_str(&got).expect("failed to deserialize network");
assert_eq!(back, network);
}
}
} }