Skip to main content

bitcoin_units/fee_rate/
serde.rs

1// SPDX-License-Identifier: CC0-1.0
2
3// Module implements standardized serde-specific trait methods.
4#![allow(missing_docs)]
5#![allow(clippy::trivially_copy_pass_by_ref)]
6#![allow(clippy::missing_errors_doc)]
7
8//! This module adds serde serialization and deserialization support for fee rates.
9//!
10//! Since there is not a default way to serialize and deserialize fee rates, multiple
11//! ways are supported and it's up to the user to decide which serialization to use.
12//!
13//! The provided modules can be used as follows:
14//!
15//! ```
16//! use serde::{Serialize, Deserialize};
17//! use bitcoin_units::{fee_rate, FeeRate};
18//!
19//! #[derive(Serialize, Deserialize)]
20//! pub struct Foo {
21//!     #[serde(with = "fee_rate::serde::as_sat_per_kwu_floor")]
22//!     pub fee_rate: FeeRate,
23//! }
24//! ```
25
26use core::convert::Infallible;
27use core::fmt;
28
29pub mod as_sat_per_kwu_floor {
30    //! Serialize and deserialize [`FeeRate`] denominated in satoshis per 1000 weight units.
31    //!
32    //! Use with `#[serde(with = "fee_rate::serde::as_sat_per_kwu_floor")]`.
33
34    use serde::{Deserialize, Deserializer, Serialize, Serializer};
35
36    use crate::{Amount, FeeRate};
37
38    pub fn serialize<S: Serializer>(f: &FeeRate, s: S) -> Result<S::Ok, S::Error> {
39        u64::serialize(&f.to_sat_per_kwu_floor(), s)
40    }
41
42    pub fn deserialize<'d, D: Deserializer<'d>>(d: D) -> Result<FeeRate, D::Error> {
43        let sat = u64::deserialize(d)?;
44        FeeRate::from_per_kwu(
45            Amount::from_sat(sat).map_err(|_| serde::de::Error::custom("amount out of range"))?,
46        )
47        .into_result()
48        .map_err(|_| serde::de::Error::custom("fee rate too big for sats/kwu"))
49    }
50
51    pub mod opt {
52        //! Serialize and deserialize [`Option<FeeRate>`] denominated in satoshis per 1000 weight units.
53        //!
54        //! Use with `#[serde(with = "fee_rate::serde::as_sat_per_kwu_floor::opt")]`.
55
56        use core::fmt;
57
58        use serde::{de, Deserializer, Serializer};
59
60        use crate::FeeRate;
61
62        #[allow(clippy::ref_option)] // API forced by serde.
63        pub fn serialize<S: Serializer>(f: &Option<FeeRate>, s: S) -> Result<S::Ok, S::Error> {
64            match *f {
65                Some(f) => s.serialize_some(&f.to_sat_per_kwu_floor()),
66                None => s.serialize_none(),
67            }
68        }
69
70        pub fn deserialize<'d, D: Deserializer<'d>>(d: D) -> Result<Option<FeeRate>, D::Error> {
71            struct VisitOpt;
72
73            impl<'de> de::Visitor<'de> for VisitOpt {
74                type Value = Option<FeeRate>;
75
76                fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
77                    write!(f, "an Option<u64>")
78                }
79
80                fn visit_none<E>(self) -> Result<Self::Value, E>
81                where
82                    E: de::Error,
83                {
84                    Ok(None)
85                }
86
87                fn visit_some<D>(self, d: D) -> Result<Self::Value, D::Error>
88                where
89                    D: Deserializer<'de>,
90                {
91                    Ok(Some(super::deserialize(d)?))
92                }
93            }
94            d.deserialize_option(VisitOpt)
95        }
96    }
97}
98
99pub mod as_sat_per_vb_floor {
100    //! Serialize and deserialize [`FeeRate`] denominated in satoshis per virtual byte.
101    //!
102    //! When serializing use floor division to convert per kwu to per virtual byte.
103    //! Use with `#[serde(with = "fee_rate::serde::as_sat_per_vb_floor")]`.
104
105    use serde::{Deserialize, Deserializer, Serialize, Serializer};
106
107    use crate::{Amount, FeeRate};
108
109    pub fn serialize<S: Serializer>(f: &FeeRate, s: S) -> Result<S::Ok, S::Error> {
110        u64::serialize(&f.to_sat_per_vb_floor(), s)
111    }
112
113    // Errors on overflow.
114    pub fn deserialize<'d, D: Deserializer<'d>>(d: D) -> Result<FeeRate, D::Error> {
115        let sat = u64::deserialize(d)?;
116        FeeRate::from_per_vb(
117            Amount::from_sat(sat).map_err(|_| serde::de::Error::custom("amount out of range"))?,
118        )
119        .into_result()
120        .map_err(|_| serde::de::Error::custom("fee rate too big for sats/vb"))
121    }
122
123    pub mod opt {
124        //! Serialize and deserialize [`Option<FeeRate>`] denominated in satoshis per virtual byte.
125        //!
126        //! When serializing use floor division to convert per kwu to per virtual byte.
127        //! Use with `#[serde(with = "fee_rate::serde::as_sat_per_vb_floor::opt")]`.
128
129        use core::fmt;
130
131        use serde::{de, Deserializer, Serializer};
132
133        use crate::fee_rate::FeeRate;
134
135        #[allow(clippy::ref_option)] // API forced by serde.
136        pub fn serialize<S: Serializer>(f: &Option<FeeRate>, s: S) -> Result<S::Ok, S::Error> {
137            match *f {
138                Some(f) => s.serialize_some(&f.to_sat_per_vb_floor()),
139                None => s.serialize_none(),
140            }
141        }
142
143        pub fn deserialize<'d, D: Deserializer<'d>>(d: D) -> Result<Option<FeeRate>, D::Error> {
144            struct VisitOpt;
145
146            impl<'de> de::Visitor<'de> for VisitOpt {
147                type Value = Option<FeeRate>;
148
149                fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
150                    write!(f, "an Option<u64>")
151                }
152
153                fn visit_none<E>(self) -> Result<Self::Value, E>
154                where
155                    E: de::Error,
156                {
157                    Ok(None)
158                }
159
160                fn visit_some<D>(self, d: D) -> Result<Self::Value, D::Error>
161                where
162                    D: Deserializer<'de>,
163                {
164                    Ok(Some(super::deserialize(d)?))
165                }
166            }
167            d.deserialize_option(VisitOpt)
168        }
169    }
170}
171
172pub mod as_sat_per_vb_ceil {
173    //! Serialize and deserialize [`FeeRate`] denominated in satoshis per virtual byte.
174    //!
175    //! When serializing use ceil division to convert per kwu to per virtual byte.
176    //! Use with `#[serde(with = "fee_rate::serde::as_sat_per_vb_ceil")]`.
177
178    use serde::{Deserialize, Deserializer, Serialize, Serializer};
179
180    use crate::{Amount, FeeRate};
181
182    pub fn serialize<S: Serializer>(f: &FeeRate, s: S) -> Result<S::Ok, S::Error> {
183        u64::serialize(&f.to_sat_per_vb_ceil(), s)
184    }
185
186    // Errors on overflow.
187    pub fn deserialize<'d, D: Deserializer<'d>>(d: D) -> Result<FeeRate, D::Error> {
188        let sat = u64::deserialize(d)?;
189        FeeRate::from_per_vb(
190            Amount::from_sat(sat).map_err(|_| serde::de::Error::custom("amount out of range"))?,
191        )
192        .into_result()
193        .map_err(|_| serde::de::Error::custom("fee rate too big for sats/vb"))
194    }
195
196    pub mod opt {
197        //! Serialize and deserialize [`Option<FeeRate>`] denominated in satoshis per virtual byte.
198        //!
199        //! When serializing use ceil division to convert per kwu to per virtual byte.
200        //! Use with `#[serde(with = "fee_rate::serde::as_sat_per_vb_ceil::opt")]`.
201
202        use core::fmt;
203
204        use serde::{de, Deserializer, Serializer};
205
206        use crate::fee_rate::FeeRate;
207
208        #[allow(clippy::ref_option)] // API forced by serde.
209        pub fn serialize<S: Serializer>(f: &Option<FeeRate>, s: S) -> Result<S::Ok, S::Error> {
210            match *f {
211                Some(f) => s.serialize_some(&f.to_sat_per_vb_ceil()),
212                None => s.serialize_none(),
213            }
214        }
215
216        pub fn deserialize<'d, D: Deserializer<'d>>(d: D) -> Result<Option<FeeRate>, D::Error> {
217            struct VisitOpt;
218
219            impl<'de> de::Visitor<'de> for VisitOpt {
220                type Value = Option<FeeRate>;
221
222                fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
223                    write!(f, "an Option<u64>")
224                }
225
226                fn visit_none<E>(self) -> Result<Self::Value, E>
227                where
228                    E: de::Error,
229                {
230                    Ok(None)
231                }
232
233                fn visit_some<D>(self, d: D) -> Result<Self::Value, D::Error>
234                where
235                    D: Deserializer<'de>,
236                {
237                    Ok(Some(super::deserialize(d)?))
238                }
239            }
240            d.deserialize_option(VisitOpt)
241        }
242    }
243}
244
245/// Overflow occurred while deserializing fee rate per virtual byte.
246#[derive(Debug, Clone, PartialEq, Eq)]
247#[non_exhaustive]
248pub struct OverflowError;
249
250impl From<Infallible> for OverflowError {
251    fn from(never: Infallible) -> Self { match never {} }
252}
253
254impl fmt::Display for OverflowError {
255    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
256        write!(f, "overflow occurred while deserializing fee rate per virtual byte")
257    }
258}
259
260#[cfg(feature = "std")]
261impl std::error::Error for OverflowError {}