diff --git a/primitives/src/tx.rs b/primitives/src/tx.rs index c550cc38..05d74dfe 100644 --- a/primitives/src/tx.rs +++ b/primitives/src/tx.rs @@ -101,7 +101,6 @@ impl FromStr for Vout { #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Display)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_BITCOIN)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] #[display("{txid}:{vout}")] pub struct Outpoint { pub txid: Txid, @@ -162,6 +161,68 @@ impl FromStr for Outpoint { } } +#[cfg(feature = "serde")] +mod _serde_outpoint { + use std::fmt; + + use serde::de::{SeqAccess, Visitor}; + use serde::ser::SerializeTuple; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + use super::*; + + impl Serialize for Outpoint { + fn serialize(&self, serializer: S) -> Result + where S: Serializer { + if serializer.is_human_readable() { + serializer.serialize_str(&self.to_string()) + } else { + let mut ser = serializer.serialize_tuple(2)?; + ser.serialize_element(&self.txid)?; + ser.serialize_element(&self.vout)?; + ser.end() + } + } + } + + impl<'de> Deserialize<'de> for Outpoint { + fn deserialize(deserializer: D) -> Result + where D: Deserializer<'de> { + use serde::de::Error; + if deserializer.is_human_readable() { + String::deserialize(deserializer).and_then(|string| { + Self::from_str(&string) + .map_err(|_| D::Error::custom("wrong outpoint string representation")) + }) + } else { + struct OutpointVisitor; + + impl<'de> Visitor<'de> for OutpointVisitor { + type Value = Outpoint; + + fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { + write!(formatter, "a transaction outpoint") + } + + fn visit_seq(self, mut seq: A) -> Result + where A: SeqAccess<'de> { + let mut outpoint = Outpoint::coinbase(); + outpoint.txid = seq + .next_element()? + .ok_or_else(|| Error::invalid_length(0, &self))?; + outpoint.vout = seq + .next_element()? + .ok_or_else(|| Error::invalid_length(1, &self))?; + Ok(outpoint) + } + } + + deserializer.deserialize_tuple(2, OutpointVisitor) + } + } + } +} + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_BITCOIN)] diff --git a/primitives/src/util.rs b/primitives/src/util.rs index d7c70642..4c919992 100644 --- a/primitives/src/util.rs +++ b/primitives/src/util.rs @@ -47,12 +47,8 @@ pub struct ChainParseError(String); #[strict_type(lib = LIB_NAME_BITCOIN, tags = repr, into_u8, try_from_u8)] #[derive(CommitEncode)] #[commit_encode(strategy = strict)] -#[cfg_attr( - feature = "serde", - derive(Serialize, Deserialize), - serde(crate = "serde_crate", rename_all = "lowercase") -)] #[repr(u8)] +// TODO: v0.11 make non_exhaustive pub enum Chain { #[default] #[display("mainnet")] @@ -68,6 +64,16 @@ pub enum Chain { Signet = 0x84, } +impl Chain { + #[inline] + pub fn is_test_chain(self) -> bool { + match self { + Chain::Bitcoin => false, + Chain::Testnet3 | Chain::Regtest | Chain::Signet => true, + } + } +} + impl FromStr for Chain { type Err = ChainParseError; @@ -83,12 +89,34 @@ impl FromStr for Chain { } } -impl Chain { - #[inline] - pub fn is_test_chain(self) -> bool { - match self { - Chain::Bitcoin => false, - Chain::Testnet3 | Chain::Regtest | Chain::Signet => true, +#[cfg(feature = "serde")] +mod _serde { + use serde::de::Error; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + use super::*; + + impl Serialize for Chain { + fn serialize(&self, serializer: S) -> Result + where S: Serializer { + if serializer.is_human_readable() { + self.to_string().serialize(serializer) + } else { + (*self as u8).serialize(serializer) + } + } + } + + impl<'de> Deserialize<'de> for Chain { + fn deserialize(deserializer: D) -> Result + where D: Deserializer<'de> { + if deserializer.is_human_readable() { + let s = String::deserialize(deserializer)?; + Chain::from_str(&s).map_err(D::Error::custom) + } else { + let v = u8::deserialize(deserializer)?; + Chain::try_from(v).map_err(D::Error::custom) + } } } }