diff --git a/units/src/amount/mod.rs b/units/src/amount/mod.rs index 7e67f454c..d1ec3c58f 100644 --- a/units/src/amount/mod.rs +++ b/units/src/amount/mod.rs @@ -6,6 +6,8 @@ //! We refer to the documentation on the types for more information. pub mod error; +#[cfg(feature = "serde")] +pub mod serde; #[cfg(test)] mod tests; @@ -1358,281 +1360,3 @@ mod private { impl SumSeal for T where T: Iterator {} impl SumSeal for T where T: Iterator {} } - -#[cfg(feature = "serde")] -pub mod serde { - // methods are implementation of a standardized serde-specific signature - #![allow(missing_docs)] - - //! This module adds serde serialization and deserialization support for Amounts. - //! - //! Since there is not a default way to serialize and deserialize Amounts, multiple - //! ways are supported and it's up to the user to decide which serialiation to use. - //! The provided modules can be used as follows: - //! - //! ```rust,ignore - //! use serde::{Serialize, Deserialize}; - //! use bitcoin_units::Amount; - //! - //! #[derive(Serialize, Deserialize)] - //! pub struct HasAmount { - //! #[serde(with = "bitcoin_units::amount::serde::as_btc")] - //! pub amount: Amount, - //! } - //! ``` - - use core::fmt; - - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - - #[cfg(feature = "alloc")] - use super::Denomination; - use super::{Amount, ParseAmountError, SignedAmount}; - - /// This trait is used only to avoid code duplication and naming collisions - /// of the different serde serialization crates. - pub trait SerdeAmount: Copy + Sized { - fn ser_sat(self, s: S, _: private::Token) -> Result; - fn des_sat<'d, D: Deserializer<'d>>(d: D, _: private::Token) -> Result; - #[cfg(feature = "alloc")] - fn ser_btc(self, s: S, _: private::Token) -> Result; - #[cfg(feature = "alloc")] - fn des_btc<'d, D: Deserializer<'d>>(d: D, _: private::Token) -> Result; - } - - mod private { - /// Controls access to the trait methods. - pub struct Token; - } - - /// This trait is only for internal Amount type serialization/deserialization - pub trait SerdeAmountForOpt: Copy + Sized + SerdeAmount { - fn type_prefix(_: private::Token) -> &'static str; - fn ser_sat_opt(self, s: S, _: private::Token) -> Result; - #[cfg(feature = "alloc")] - fn ser_btc_opt(self, s: S, _: private::Token) -> Result; - } - - struct DisplayFullError(ParseAmountError); - - #[cfg(feature = "std")] - impl fmt::Display for DisplayFullError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use std::error::Error; - - fmt::Display::fmt(&self.0, f)?; - let mut source_opt = self.0.source(); - while let Some(source) = source_opt { - write!(f, ": {}", source)?; - source_opt = source.source(); - } - Ok(()) - } - } - - #[cfg(not(feature = "std"))] - impl fmt::Display for DisplayFullError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) } - } - - impl SerdeAmount for Amount { - fn ser_sat(self, s: S, _: private::Token) -> Result { - u64::serialize(&self.to_sat(), s) - } - fn des_sat<'d, D: Deserializer<'d>>(d: D, _: private::Token) -> Result { - Ok(Amount::from_sat(u64::deserialize(d)?)) - } - #[cfg(feature = "alloc")] - fn ser_btc(self, s: S, _: private::Token) -> Result { - f64::serialize(&self.to_float_in(Denomination::Bitcoin), s) - } - #[cfg(feature = "alloc")] - fn des_btc<'d, D: Deserializer<'d>>(d: D, _: private::Token) -> Result { - use serde::de::Error; - Amount::from_btc(f64::deserialize(d)?) - .map_err(DisplayFullError) - .map_err(D::Error::custom) - } - } - - impl SerdeAmountForOpt for Amount { - fn type_prefix(_: private::Token) -> &'static str { "u" } - fn ser_sat_opt(self, s: S, _: private::Token) -> Result { - s.serialize_some(&self.to_sat()) - } - #[cfg(feature = "alloc")] - fn ser_btc_opt(self, s: S, _: private::Token) -> Result { - s.serialize_some(&self.to_btc()) - } - } - - impl SerdeAmount for SignedAmount { - fn ser_sat(self, s: S, _: private::Token) -> Result { - i64::serialize(&self.to_sat(), s) - } - fn des_sat<'d, D: Deserializer<'d>>(d: D, _: private::Token) -> Result { - Ok(SignedAmount::from_sat(i64::deserialize(d)?)) - } - #[cfg(feature = "alloc")] - fn ser_btc(self, s: S, _: private::Token) -> Result { - f64::serialize(&self.to_float_in(Denomination::Bitcoin), s) - } - #[cfg(feature = "alloc")] - fn des_btc<'d, D: Deserializer<'d>>(d: D, _: private::Token) -> Result { - use serde::de::Error; - SignedAmount::from_btc(f64::deserialize(d)?) - .map_err(DisplayFullError) - .map_err(D::Error::custom) - } - } - - impl SerdeAmountForOpt for SignedAmount { - fn type_prefix(_: private::Token) -> &'static str { "i" } - fn ser_sat_opt(self, s: S, _: private::Token) -> Result { - s.serialize_some(&self.to_sat()) - } - #[cfg(feature = "alloc")] - fn ser_btc_opt(self, s: S, _: private::Token) -> Result { - s.serialize_some(&self.to_btc()) - } - } - - pub mod as_sat { - //! Serialize and deserialize [`Amount`](crate::Amount) as real numbers denominated in satoshi. - //! Use with `#[serde(with = "amount::serde::as_sat")]`. - - use serde::{Deserializer, Serializer}; - - use super::private; - use crate::amount::serde::SerdeAmount; - - pub fn serialize(a: &A, s: S) -> Result { - a.ser_sat(s, private::Token) - } - - pub fn deserialize<'d, A: SerdeAmount, D: Deserializer<'d>>(d: D) -> Result { - A::des_sat(d, private::Token) - } - - pub mod opt { - //! Serialize and deserialize [`Option`](crate::Amount) as real numbers denominated in satoshi. - //! Use with `#[serde(default, with = "amount::serde::as_sat::opt")]`. - - use core::fmt; - use core::marker::PhantomData; - - use serde::{de, Deserializer, Serializer}; - - use super::private; - use crate::amount::serde::SerdeAmountForOpt; - - pub fn serialize( - a: &Option, - s: S, - ) -> Result { - match *a { - Some(a) => a.ser_sat_opt(s, private::Token), - None => s.serialize_none(), - } - } - - pub fn deserialize<'d, A: SerdeAmountForOpt, D: Deserializer<'d>>( - d: D, - ) -> Result, D::Error> { - struct VisitOptAmt(PhantomData); - - impl<'de, X: SerdeAmountForOpt> de::Visitor<'de> for VisitOptAmt { - type Value = Option; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "An Option<{}64>", X::type_prefix(private::Token)) - } - - fn visit_none(self) -> Result - where - E: de::Error, - { - Ok(None) - } - fn visit_some(self, d: D) -> Result - where - D: Deserializer<'de>, - { - Ok(Some(X::des_sat(d, private::Token)?)) - } - } - d.deserialize_option(VisitOptAmt::(PhantomData)) - } - } - } - - #[cfg(feature = "alloc")] - pub mod as_btc { - //! Serialize and deserialize [`Amount`](crate::Amount) as JSON numbers denominated in BTC. - //! Use with `#[serde(with = "amount::serde::as_btc")]`. - - use serde::{Deserializer, Serializer}; - - use super::private; - use crate::amount::serde::SerdeAmount; - - pub fn serialize(a: &A, s: S) -> Result { - a.ser_btc(s, private::Token) - } - - pub fn deserialize<'d, A: SerdeAmount, D: Deserializer<'d>>(d: D) -> Result { - A::des_btc(d, private::Token) - } - - pub mod opt { - //! Serialize and deserialize `Option` as JSON numbers denominated in BTC. - //! Use with `#[serde(default, with = "amount::serde::as_btc::opt")]`. - - use core::fmt; - use core::marker::PhantomData; - - use serde::{de, Deserializer, Serializer}; - - use super::private; - use crate::amount::serde::SerdeAmountForOpt; - - pub fn serialize( - a: &Option, - s: S, - ) -> Result { - match *a { - Some(a) => a.ser_btc_opt(s, private::Token), - None => s.serialize_none(), - } - } - - pub fn deserialize<'d, A: SerdeAmountForOpt, D: Deserializer<'d>>( - d: D, - ) -> Result, D::Error> { - struct VisitOptAmt(PhantomData); - - impl<'de, X: SerdeAmountForOpt> de::Visitor<'de> for VisitOptAmt { - type Value = Option; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "An Option") - } - - fn visit_none(self) -> Result - where - E: de::Error, - { - Ok(None) - } - fn visit_some(self, d: D) -> Result - where - D: Deserializer<'de>, - { - Ok(Some(X::des_btc(d, private::Token)?)) - } - } - d.deserialize_option(VisitOptAmt::(PhantomData)) - } - } - } -} diff --git a/units/src/amount/serde.rs b/units/src/amount/serde.rs new file mode 100644 index 000000000..6a1008cae --- /dev/null +++ b/units/src/amount/serde.rs @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: CC0-1.0 + +// methods are implementation of a standardized serde-specific signature +#![allow(missing_docs)] + +//! This module adds serde serialization and deserialization support for Amounts. +//! +//! Since there is not a default way to serialize and deserialize Amounts, multiple +//! ways are supported and it's up to the user to decide which serialiation to use. +//! The provided modules can be used as follows: +//! +//! ```rust,ignore +//! use serde::{Serialize, Deserialize}; +//! use bitcoin_units::Amount; +//! +//! #[derive(Serialize, Deserialize)] +//! pub struct HasAmount { +//! #[serde(with = "bitcoin_units::amount::serde::as_btc")] +//! pub amount: Amount, +//! } +//! ``` + +use core::fmt; + +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +#[cfg(feature = "alloc")] +use super::Denomination; +use super::{Amount, ParseAmountError, SignedAmount}; + +/// This trait is used only to avoid code duplication and naming collisions +/// of the different serde serialization crates. +pub trait SerdeAmount: Copy + Sized { + fn ser_sat(self, s: S, _: private::Token) -> Result; + fn des_sat<'d, D: Deserializer<'d>>(d: D, _: private::Token) -> Result; + #[cfg(feature = "alloc")] + fn ser_btc(self, s: S, _: private::Token) -> Result; + #[cfg(feature = "alloc")] + fn des_btc<'d, D: Deserializer<'d>>(d: D, _: private::Token) -> Result; +} + +mod private { + /// Controls access to the trait methods. + pub struct Token; +} + +/// This trait is only for internal Amount type serialization/deserialization +pub trait SerdeAmountForOpt: Copy + Sized + SerdeAmount { + fn type_prefix(_: private::Token) -> &'static str; + fn ser_sat_opt(self, s: S, _: private::Token) -> Result; + #[cfg(feature = "alloc")] + fn ser_btc_opt(self, s: S, _: private::Token) -> Result; +} + +struct DisplayFullError(ParseAmountError); + +#[cfg(feature = "std")] +impl fmt::Display for DisplayFullError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use std::error::Error; + + fmt::Display::fmt(&self.0, f)?; + let mut source_opt = self.0.source(); + while let Some(source) = source_opt { + write!(f, ": {}", source)?; + source_opt = source.source(); + } + Ok(()) + } +} + +#[cfg(not(feature = "std"))] +impl fmt::Display for DisplayFullError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) } +} + +impl SerdeAmount for Amount { + fn ser_sat(self, s: S, _: private::Token) -> Result { + u64::serialize(&self.to_sat(), s) + } + fn des_sat<'d, D: Deserializer<'d>>(d: D, _: private::Token) -> Result { + Ok(Amount::from_sat(u64::deserialize(d)?)) + } + #[cfg(feature = "alloc")] + fn ser_btc(self, s: S, _: private::Token) -> Result { + f64::serialize(&self.to_float_in(Denomination::Bitcoin), s) + } + #[cfg(feature = "alloc")] + fn des_btc<'d, D: Deserializer<'d>>(d: D, _: private::Token) -> Result { + use serde::de::Error; + Amount::from_btc(f64::deserialize(d)?) + .map_err(DisplayFullError) + .map_err(D::Error::custom) + } +} + +impl SerdeAmountForOpt for Amount { + fn type_prefix(_: private::Token) -> &'static str { "u" } + fn ser_sat_opt(self, s: S, _: private::Token) -> Result { + s.serialize_some(&self.to_sat()) + } + #[cfg(feature = "alloc")] + fn ser_btc_opt(self, s: S, _: private::Token) -> Result { + s.serialize_some(&self.to_btc()) + } +} + +impl SerdeAmount for SignedAmount { + fn ser_sat(self, s: S, _: private::Token) -> Result { + i64::serialize(&self.to_sat(), s) + } + fn des_sat<'d, D: Deserializer<'d>>(d: D, _: private::Token) -> Result { + Ok(SignedAmount::from_sat(i64::deserialize(d)?)) + } + #[cfg(feature = "alloc")] + fn ser_btc(self, s: S, _: private::Token) -> Result { + f64::serialize(&self.to_float_in(Denomination::Bitcoin), s) + } + #[cfg(feature = "alloc")] + fn des_btc<'d, D: Deserializer<'d>>(d: D, _: private::Token) -> Result { + use serde::de::Error; + SignedAmount::from_btc(f64::deserialize(d)?) + .map_err(DisplayFullError) + .map_err(D::Error::custom) + } +} + +impl SerdeAmountForOpt for SignedAmount { + fn type_prefix(_: private::Token) -> &'static str { "i" } + fn ser_sat_opt(self, s: S, _: private::Token) -> Result { + s.serialize_some(&self.to_sat()) + } + #[cfg(feature = "alloc")] + fn ser_btc_opt(self, s: S, _: private::Token) -> Result { + s.serialize_some(&self.to_btc()) + } +} + +pub mod as_sat { + //! Serialize and deserialize [`Amount`](crate::Amount) as real numbers denominated in satoshi. + //! Use with `#[serde(with = "amount::serde::as_sat")]`. + + use serde::{Deserializer, Serializer}; + + use super::private; + use crate::amount::serde::SerdeAmount; + + pub fn serialize(a: &A, s: S) -> Result { + a.ser_sat(s, private::Token) + } + + pub fn deserialize<'d, A: SerdeAmount, D: Deserializer<'d>>(d: D) -> Result { + A::des_sat(d, private::Token) + } + + pub mod opt { + //! Serialize and deserialize [`Option`](crate::Amount) as real numbers denominated in satoshi. + //! Use with `#[serde(default, with = "amount::serde::as_sat::opt")]`. + + use core::fmt; + use core::marker::PhantomData; + + use serde::{de, Deserializer, Serializer}; + + use super::private; + use crate::amount::serde::SerdeAmountForOpt; + + pub fn serialize( + a: &Option, + s: S, + ) -> Result { + match *a { + Some(a) => a.ser_sat_opt(s, private::Token), + None => s.serialize_none(), + } + } + + pub fn deserialize<'d, A: SerdeAmountForOpt, D: Deserializer<'d>>( + d: D, + ) -> Result, D::Error> { + struct VisitOptAmt(PhantomData); + + impl<'de, X: SerdeAmountForOpt> de::Visitor<'de> for VisitOptAmt { + type Value = Option; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "An Option<{}64>", X::type_prefix(private::Token)) + } + + fn visit_none(self) -> Result + where + E: de::Error, + { + Ok(None) + } + fn visit_some(self, d: D) -> Result + where + D: Deserializer<'de>, + { + Ok(Some(X::des_sat(d, private::Token)?)) + } + } + d.deserialize_option(VisitOptAmt::(PhantomData)) + } + } +} + +#[cfg(feature = "alloc")] +pub mod as_btc { + //! Serialize and deserialize [`Amount`](crate::Amount) as JSON numbers denominated in BTC. + //! Use with `#[serde(with = "amount::serde::as_btc")]`. + + use serde::{Deserializer, Serializer}; + + use super::private; + use crate::amount::serde::SerdeAmount; + + pub fn serialize(a: &A, s: S) -> Result { + a.ser_btc(s, private::Token) + } + + pub fn deserialize<'d, A: SerdeAmount, D: Deserializer<'d>>(d: D) -> Result { + A::des_btc(d, private::Token) + } + + pub mod opt { + //! Serialize and deserialize `Option` as JSON numbers denominated in BTC. + //! Use with `#[serde(default, with = "amount::serde::as_btc::opt")]`. + + use core::fmt; + use core::marker::PhantomData; + + use serde::{de, Deserializer, Serializer}; + + use super::private; + use crate::amount::serde::SerdeAmountForOpt; + + pub fn serialize( + a: &Option, + s: S, + ) -> Result { + match *a { + Some(a) => a.ser_btc_opt(s, private::Token), + None => s.serialize_none(), + } + } + + pub fn deserialize<'d, A: SerdeAmountForOpt, D: Deserializer<'d>>( + d: D, + ) -> Result, D::Error> { + struct VisitOptAmt(PhantomData); + + impl<'de, X: SerdeAmountForOpt> de::Visitor<'de> for VisitOptAmt { + type Value = Option; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "An Option") + } + + fn visit_none(self) -> Result + where + E: de::Error, + { + Ok(None) + } + fn visit_some(self, d: D) -> Result + where + D: Deserializer<'de>, + { + Ok(Some(X::des_btc(d, private::Token)?)) + } + } + d.deserialize_option(VisitOptAmt::(PhantomData)) + } + } +}