Skip to content

Commit

Permalink
Merge pull request #58 from BP-WG/serde
Browse files Browse the repository at this point in the history
Add serde support for text serialization
  • Loading branch information
dr-orlovsky authored Oct 7, 2023
2 parents 4ce97a5 + 5d73e24 commit 9ed4c69
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 12 deletions.
63 changes: 62 additions & 1 deletion primitives/src/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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<D>(deserializer: D) -> Result<Self, D::Error>
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<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
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)]
Expand Down
50 changes: 39 additions & 11 deletions primitives/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand All @@ -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;

Expand All @@ -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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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<D>(deserializer: D) -> Result<Self, D::Error>
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)
}
}
}
}

0 comments on commit 9ed4c69

Please sign in to comment.