From 4a2b13fcde0b2b64568533e98e970f66eb8b6e10 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 20 Jun 2024 13:59:57 +1000 Subject: [PATCH 1/2] internals: Feature gate whole serde module Instead of feature gating the individual code blocks just feature gate the whole `serde` module. --- internals/src/lib.rs | 1 + internals/src/serde.rs | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/internals/src/lib.rs b/internals/src/lib.rs index d9af9bac0..d86a1c3e8 100644 --- a/internals/src/lib.rs +++ b/internals/src/lib.rs @@ -26,4 +26,5 @@ pub mod const_tools; pub mod error; pub mod macros; mod parse; +#[cfg(feature = "serde")] pub mod serde; diff --git a/internals/src/serde.rs b/internals/src/serde.rs index de79756a0..7fd8d5f18 100644 --- a/internals/src/serde.rs +++ b/internals/src/serde.rs @@ -1,6 +1,5 @@ //! Contains extensions of `serde` and internal reexports. -#[cfg(feature = "serde")] #[doc(hidden)] pub use serde::{de, ser, Deserialize, Deserializer, Serialize, Serializer}; @@ -8,7 +7,6 @@ pub use serde::{de, ser, Deserialize, Deserializer, Serialize, Serializer}; /// /// This is used in [`Deserialize`] implementations to convert specialized errors into serde /// errors. -#[cfg(feature = "serde")] pub trait IntoDeError: Sized { /// Converts to deserializer error possibly outputting vague message. /// @@ -28,7 +26,6 @@ pub trait IntoDeError: Sized { } } -#[cfg(feature = "serde")] mod impls { use super::*; From 865ba3fc39d467468a7b8a9d61f14d15807220ac Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 20 Jun 2024 14:25:43 +1000 Subject: [PATCH 2/2] Move serde string macros to internals The macros are internal things and can live in `internals`. This will help with future crate smashing. --- bitcoin/src/address/mod.rs | 2 +- bitcoin/src/bip32.rs | 6 +- bitcoin/src/blockdata/transaction.rs | 2 +- bitcoin/src/crypto/sighash.rs | 4 +- bitcoin/src/serde_utils.rs | 223 ------------------------- internals/src/serde.rs | 234 +++++++++++++++++++++++++++ 6 files changed, 241 insertions(+), 230 deletions(-) diff --git a/bitcoin/src/address/mod.rs b/bitcoin/src/address/mod.rs index 31f4cf8fa..f181b74b1 100644 --- a/bitcoin/src/address/mod.rs +++ b/bitcoin/src/address/mod.rs @@ -365,7 +365,7 @@ impl fmt::Display for DisplayUnchecked<'_, N> { } #[cfg(feature = "serde")] -crate::serde_utils::serde_string_deserialize_impl!(Address, "a Bitcoin address"); +internals::serde_string_deserialize_impl!(Address, "a Bitcoin address"); #[cfg(feature = "serde")] impl serde::Serialize for Address { diff --git a/bitcoin/src/bip32.rs b/bitcoin/src/bip32.rs index 6199b3a34..3a165f215 100644 --- a/bitcoin/src/bip32.rs +++ b/bitcoin/src/bip32.rs @@ -77,7 +77,7 @@ pub struct Xpriv { pub chain_code: ChainCode, } #[cfg(feature = "serde")] -crate::serde_utils::serde_string_impl!(Xpriv, "a BIP-32 extended private key"); +internals::serde_string_impl!(Xpriv, "a BIP-32 extended private key"); #[cfg(not(feature = "std"))] impl fmt::Debug for Xpriv { @@ -110,7 +110,7 @@ pub struct Xpub { pub chain_code: ChainCode, } #[cfg(feature = "serde")] -crate::serde_utils::serde_string_impl!(Xpub, "a BIP-32 extended public key"); +internals::serde_string_impl!(Xpub, "a BIP-32 extended public key"); /// A child number for a derived key #[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)] @@ -259,7 +259,7 @@ pub trait IntoDerivationPath { pub struct DerivationPath(Vec); #[cfg(feature = "serde")] -crate::serde_utils::serde_string_impl!(DerivationPath, "a BIP-32 derivation path"); +internals::serde_string_impl!(DerivationPath, "a BIP-32 derivation path"); impl Index for DerivationPath where diff --git a/bitcoin/src/blockdata/transaction.rs b/bitcoin/src/blockdata/transaction.rs index 132d7a71d..3268633de 100644 --- a/bitcoin/src/blockdata/transaction.rs +++ b/bitcoin/src/blockdata/transaction.rs @@ -68,7 +68,7 @@ pub struct OutPoint { pub vout: u32, } #[cfg(feature = "serde")] -crate::serde_utils::serde_struct_human_string_impl!(OutPoint, "an OutPoint", txid, vout); +internals::serde_struct_human_string_impl!(OutPoint, "an OutPoint", txid, vout); impl OutPoint { /// The number of bytes that an outpoint contributes to the size of a transaction. diff --git a/bitcoin/src/crypto/sighash.rs b/bitcoin/src/crypto/sighash.rs index f68fb56bc..0f58bfcfe 100644 --- a/bitcoin/src/crypto/sighash.rs +++ b/bitcoin/src/crypto/sighash.rs @@ -160,7 +160,7 @@ pub enum TapSighashType { SinglePlusAnyoneCanPay = 0x83, } #[cfg(feature = "serde")] -crate::serde_utils::serde_string_impl!(TapSighashType, "a TapSighashType data"); +internals::serde_string_impl!(TapSighashType, "a TapSighashType data"); impl fmt::Display for TapSighashType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -348,7 +348,7 @@ pub enum EcdsaSighashType { SinglePlusAnyoneCanPay = 0x83, } #[cfg(feature = "serde")] -crate::serde_utils::serde_string_impl!(EcdsaSighashType, "a EcdsaSighashType data"); +internals::serde_string_impl!(EcdsaSighashType, "a EcdsaSighashType data"); impl fmt::Display for EcdsaSighashType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/bitcoin/src/serde_utils.rs b/bitcoin/src/serde_utils.rs index 7b83ea678..22c44d301 100644 --- a/bitcoin/src/serde_utils.rs +++ b/bitcoin/src/serde_utils.rs @@ -298,226 +298,3 @@ pub mod hex_bytes { } } } - -macro_rules! serde_string_serialize_impl { - ($name:ty, $expecting:literal) => { - impl $crate::serde::Serialize for $name { - fn serialize(&self, serializer: S) -> core::result::Result - where - S: $crate::serde::Serializer, - { - serializer.collect_str(&self) - } - } - }; -} - -macro_rules! serde_string_deserialize_impl { - ($name:ty, $expecting:literal) => { - impl<'de> $crate::serde::Deserialize<'de> for $name { - fn deserialize(deserializer: D) -> core::result::Result<$name, D::Error> - where - D: $crate::serde::de::Deserializer<'de>, - { - use core::fmt::Formatter; - - struct Visitor; - impl<'de> $crate::serde::de::Visitor<'de> for Visitor { - type Value = $name; - - fn expecting(&self, f: &mut Formatter) -> core::fmt::Result { - f.write_str($expecting) - } - - fn visit_str(self, v: &str) -> core::result::Result - where - E: $crate::serde::de::Error, - { - v.parse::<$name>().map_err(E::custom) - } - } - - deserializer.deserialize_str(Visitor) - } - } - }; -} - -macro_rules! serde_string_impl { - ($name:ty, $expecting:literal) => { - $crate::serde_utils::serde_string_deserialize_impl!($name, $expecting); - $crate::serde_utils::serde_string_serialize_impl!($name, $expecting); - }; -} -pub(crate) use {serde_string_deserialize_impl, serde_string_impl, serde_string_serialize_impl}; - -/// A combination macro where the human-readable serialization is done like -/// serde_string_impl and the non-human-readable impl is done as a struct. -macro_rules! serde_struct_human_string_impl { - ($name:ident, $expecting:literal, $($fe:ident),*) => ( - impl<'de> $crate::serde::Deserialize<'de> for $name { - fn deserialize(deserializer: D) -> core::result::Result<$name, D::Error> - where - D: $crate::serde::de::Deserializer<'de>, - { - if deserializer.is_human_readable() { - use core::fmt::Formatter; - use core::str::FromStr; - - struct Visitor; - impl<'de> $crate::serde::de::Visitor<'de> for Visitor { - type Value = $name; - - fn expecting(&self, f: &mut Formatter) -> core::fmt::Result { - f.write_str($expecting) - } - - fn visit_str(self, v: &str) -> core::result::Result - where - E: $crate::serde::de::Error, - { - $name::from_str(v).map_err(E::custom) - } - - } - - deserializer.deserialize_str(Visitor) - } else { - use core::fmt::Formatter; - use $crate::serde::de::IgnoredAny; - - #[allow(non_camel_case_types)] - enum Enum { Unknown__Field, $($fe),* } - - struct EnumVisitor; - impl<'de> $crate::serde::de::Visitor<'de> for EnumVisitor { - type Value = Enum; - - fn expecting(&self, f: &mut Formatter) -> core::fmt::Result { - f.write_str("a field name") - } - - fn visit_str(self, v: &str) -> core::result::Result - where - E: $crate::serde::de::Error, - { - match v { - $( - stringify!($fe) => Ok(Enum::$fe) - ),*, - _ => Ok(Enum::Unknown__Field) - } - } - } - - impl<'de> $crate::serde::Deserialize<'de> for Enum { - fn deserialize(deserializer: D) -> core::result::Result - where - D: $crate::serde::de::Deserializer<'de>, - { - deserializer.deserialize_str(EnumVisitor) - } - } - - struct Visitor; - - impl<'de> $crate::serde::de::Visitor<'de> for Visitor { - type Value = $name; - - fn expecting(&self, f: &mut Formatter) -> core::fmt::Result { - f.write_str("a struct") - } - - fn visit_seq(self, mut seq: V) -> core::result::Result - where - V: $crate::serde::de::SeqAccess<'de>, - { - use $crate::serde::de::Error; - - let length = 0; - $( - let $fe = seq.next_element()?.ok_or_else(|| { - Error::invalid_length(length, &self) - })?; - #[allow(unused_variables)] - let length = length + 1; - )* - - let ret = $name { - $($fe),* - }; - - Ok(ret) - } - - fn visit_map(self, mut map: A) -> core::result::Result - where - A: $crate::serde::de::MapAccess<'de>, - { - use $crate::serde::de::Error; - - $(let mut $fe = None;)* - - loop { - match map.next_key::()? { - Some(Enum::Unknown__Field) => { - map.next_value::()?; - } - $( - Some(Enum::$fe) => { - $fe = Some(map.next_value()?); - } - )* - None => { break; } - } - } - - $( - let $fe = match $fe { - Some(x) => x, - None => return Err(A::Error::missing_field(stringify!($fe))), - }; - )* - - let ret = $name { - $($fe),* - }; - - Ok(ret) - } - } - // end type defs - - static FIELDS: &'static [&'static str] = &[$(stringify!($fe)),*]; - - deserializer.deserialize_struct(stringify!($name), FIELDS, Visitor) - } - } - } - - impl $crate::serde::Serialize for $name { - fn serialize(&self, serializer: S) -> core::result::Result - where - S: $crate::serde::Serializer, - { - if serializer.is_human_readable() { - serializer.collect_str(&self) - } else { - use $crate::serde::ser::SerializeStruct; - - // Only used to get the struct length. - static FIELDS: &'static [&'static str] = &[$(stringify!($fe)),*]; - - let mut st = serializer.serialize_struct(stringify!($name), FIELDS.len())?; - - $( - st.serialize_field(stringify!($fe), &self.$fe)?; - )* - - st.end() - } - } - } - ) -} -pub(crate) use serde_struct_human_string_impl; diff --git a/internals/src/serde.rs b/internals/src/serde.rs index 7fd8d5f18..c7afa0d37 100644 --- a/internals/src/serde.rs +++ b/internals/src/serde.rs @@ -59,3 +59,237 @@ mod impls { } } } + +/// Implements `serde::Serialize` by way of `Display`. +/// +/// `$name` is required to implement `core::fmt::Display`. +#[macro_export] +macro_rules! serde_string_serialize_impl { + ($name:ty, $expecting:literal) => { + impl $crate::serde::Serialize for $name { + fn serialize(&self, serializer: S) -> core::result::Result + where + S: $crate::serde::Serializer, + { + serializer.collect_str(&self) + } + } + }; +} + +/// Implements `serde::Deserialize` by way of `FromStr`. +/// +/// `$name` is required to implement `core::str::FromStr`. +#[macro_export] +macro_rules! serde_string_deserialize_impl { + ($name:ty, $expecting:literal) => { + impl<'de> $crate::serde::Deserialize<'de> for $name { + fn deserialize(deserializer: D) -> core::result::Result<$name, D::Error> + where + D: $crate::serde::de::Deserializer<'de>, + { + use core::fmt::Formatter; + + struct Visitor; + impl<'de> $crate::serde::de::Visitor<'de> for Visitor { + type Value = $name; + + fn expecting(&self, f: &mut Formatter) -> core::fmt::Result { + f.write_str($expecting) + } + + fn visit_str(self, v: &str) -> core::result::Result + where + E: $crate::serde::de::Error, + { + v.parse::<$name>().map_err(E::custom) + } + } + + deserializer.deserialize_str(Visitor) + } + } + }; +} + +/// Implements `serde::Serialize` and `Deserialize` by way of `Display` and `FromStr` respectively. +/// +/// `$name` is required to implement `core::fmt::Display` and `core::str::FromStr`. +#[macro_export] +macro_rules! serde_string_impl { + ($name:ty, $expecting:literal) => { + $crate::serde_string_deserialize_impl!($name, $expecting); + $crate::serde_string_serialize_impl!($name, $expecting); + }; +} + +/// A combination macro where the human-readable serialization is done like +/// serde_string_impl and the non-human-readable impl is done as a struct. +#[macro_export] +macro_rules! serde_struct_human_string_impl { + ($name:ident, $expecting:literal, $($fe:ident),*) => ( + impl<'de> $crate::serde::Deserialize<'de> for $name { + fn deserialize(deserializer: D) -> core::result::Result<$name, D::Error> + where + D: $crate::serde::de::Deserializer<'de>, + { + if deserializer.is_human_readable() { + use core::fmt::Formatter; + use core::str::FromStr; + + struct Visitor; + impl<'de> $crate::serde::de::Visitor<'de> for Visitor { + type Value = $name; + + fn expecting(&self, f: &mut Formatter) -> core::fmt::Result { + f.write_str($expecting) + } + + fn visit_str(self, v: &str) -> core::result::Result + where + E: $crate::serde::de::Error, + { + $name::from_str(v).map_err(E::custom) + } + + } + + deserializer.deserialize_str(Visitor) + } else { + use core::fmt::Formatter; + use $crate::serde::de::IgnoredAny; + + #[allow(non_camel_case_types)] + enum Enum { Unknown__Field, $($fe),* } + + struct EnumVisitor; + impl<'de> $crate::serde::de::Visitor<'de> for EnumVisitor { + type Value = Enum; + + fn expecting(&self, f: &mut Formatter) -> core::fmt::Result { + f.write_str("a field name") + } + + fn visit_str(self, v: &str) -> core::result::Result + where + E: $crate::serde::de::Error, + { + match v { + $( + stringify!($fe) => Ok(Enum::$fe) + ),*, + _ => Ok(Enum::Unknown__Field) + } + } + } + + impl<'de> $crate::serde::Deserialize<'de> for Enum { + fn deserialize(deserializer: D) -> core::result::Result + where + D: $crate::serde::de::Deserializer<'de>, + { + deserializer.deserialize_str(EnumVisitor) + } + } + + struct Visitor; + + impl<'de> $crate::serde::de::Visitor<'de> for Visitor { + type Value = $name; + + fn expecting(&self, f: &mut Formatter) -> core::fmt::Result { + f.write_str("a struct") + } + + fn visit_seq(self, mut seq: V) -> core::result::Result + where + V: $crate::serde::de::SeqAccess<'de>, + { + use $crate::serde::de::Error; + + let length = 0; + $( + let $fe = seq.next_element()?.ok_or_else(|| { + Error::invalid_length(length, &self) + })?; + #[allow(unused_variables)] + let length = length + 1; + )* + + let ret = $name { + $($fe),* + }; + + Ok(ret) + } + + fn visit_map(self, mut map: A) -> core::result::Result + where + A: $crate::serde::de::MapAccess<'de>, + { + use $crate::serde::de::Error; + + $(let mut $fe = None;)* + + loop { + match map.next_key::()? { + Some(Enum::Unknown__Field) => { + map.next_value::()?; + } + $( + Some(Enum::$fe) => { + $fe = Some(map.next_value()?); + } + )* + None => { break; } + } + } + + $( + let $fe = match $fe { + Some(x) => x, + None => return Err(A::Error::missing_field(stringify!($fe))), + }; + )* + + let ret = $name { + $($fe),* + }; + + Ok(ret) + } + } + // end type defs + + static FIELDS: &'static [&'static str] = &[$(stringify!($fe)),*]; + + deserializer.deserialize_struct(stringify!($name), FIELDS, Visitor) + } + } + } + + impl $crate::serde::Serialize for $name { + fn serialize(&self, serializer: S) -> core::result::Result + where + S: $crate::serde::Serializer, + { + if serializer.is_human_readable() { + serializer.collect_str(&self) + } else { + use $crate::serde::ser::SerializeStruct; + + // Only used to get the struct length. + static FIELDS: &'static [&'static str] = &[$(stringify!($fe)),*]; + + let mut st = serializer.serialize_struct(stringify!($name), FIELDS.len())?; + + $( + st.serialize_field(stringify!($fe), &self.$fe)?; + )* + + st.end() + } + } + } + ) +}