units: Serialize Option<T> via combinators
What changed, and why it matters
This commit is a routine code cleanup in the rust-bitcoin library. It rewrites how optional amounts and fee rates are converted for JSON/serialization output to use more idiomatic Rust chaining (`.map(...).serialize(s)`) instead of explicit `match` blocks. There is no change to the actual serialized data or to security behavior.
No security action required. Treat as a normal refactoring/code-quality change.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch refactors serde serialization helpers in units/src/amount/serde.rs and units/src/fee_rate/serde.rs. It replaces explicit match *a { Some(...) => s.serialize_some(...), None => s.serialize_none() } patterns with chained Option::map calls ending in .serialize(s). For as_sat::opt and as_sat::vec it also inlines Into::into and SignedAmount::to_sat via iterator/map chains. The imports add serde::Serialize where needed. The transformation is behavior-preserving: Option<T>::serialize delegates to serialize_some/serialize_none exactly as before.
Changed components
units/src/amount/serde.rsunits/src/fee_rate/serde.rsInspect captured patch +13 / −37
diff --git a/units/src/amount/serde.rs b/units/src/amount/serde.rs
index 81b9794f..97cd8956 100644
--- a/units/src/amount/serde.rs
+++ b/units/src/amount/serde.rs
@@ -94,7 +94,7 @@ pub mod as_sat {
use core::fmt;
use core::marker::PhantomData;
- use serde::{de, Deserializer, Serializer};
+ use serde::{de, Deserializer, Serialize, Serializer};
use crate::SignedAmount;
@@ -104,13 +104,7 @@ pub mod as_sat {
where
A: Into<SignedAmount> + Copy,
{
- match *a {
- Some(a) => {
- let amount: SignedAmount = a.into();
- s.serialize_some(&amount.to_sat())
- }
- None => s.serialize_none(),
- }
+ a.map(Into::into).map(SignedAmount::to_sat).serialize(s)
}
pub fn deserialize<'d, A, D: Deserializer<'d>>(d: D) -> Result<Option<A>, D::Error>
@@ -171,10 +165,7 @@ pub mod as_sat {
where
A: Into<SignedAmount> + Copy,
{
- s.collect_seq(a.iter().map(|amount| {
- let signed_amount: SignedAmount = (*amount).into();
- signed_amount.to_sat()
- }))
+ s.collect_seq(a.iter().map(|&amount| amount.into()).map(SignedAmount::to_sat))
}
pub fn deserialize<'d, A, D: Deserializer<'d>>(d: D) -> Result<Vec<A>, D::Error>
@@ -272,11 +263,8 @@ pub mod as_btc {
where
A: Into<SignedAmount> + Copy,
{
- match *a {
- Some(a) => {
- let amount: SignedAmount = a.into();
- f64::serialize(&amount.to_float_in(Denomination::Bitcoin), s)
- }
+ match a.map(Into::into).map(|amt| amt.to_float_in(Denomination::Bitcoin)) {
+ Some(a) => f64::serialize(&a, s),
None => s.serialize_none(),
}
}
@@ -441,11 +429,8 @@ pub mod as_str {
where
A: Into<SignedAmount> + Copy,
{
- match *a {
- Some(a) => {
- let amount: SignedAmount = a.into();
- str::serialize(&amount.to_string_in(Denomination::Bitcoin), s)
- }
+ match a.map(Into::into).map(|amt| amt.to_string_in(Denomination::Bitcoin)) {
+ Some(a) => str::serialize(&a, s),
None => s.serialize_none(),
}
}
diff --git a/units/src/fee_rate/serde.rs b/units/src/fee_rate/serde.rs
index 1ba3e12a..52a1ade8 100644
--- a/units/src/fee_rate/serde.rs
+++ b/units/src/fee_rate/serde.rs
@@ -57,17 +57,14 @@ pub mod as_sat_per_kwu_floor {
use core::fmt;
- use serde::{de, Deserializer, Serializer};
+ use serde::{de, Deserializer, Serialize, Serializer};
use crate::FeeRate;
#[inline]
#[allow(clippy::ref_option)] // API forced by serde.
pub fn serialize<S: Serializer>(f: &Option<FeeRate>, s: S) -> Result<S::Ok, S::Error> {
- match *f {
- Some(f) => s.serialize_some(&f.to_sat_per_kwu_floor()),
- None => s.serialize_none(),
- }
+ f.map(FeeRate::to_sat_per_kwu_floor).serialize(s)
}
pub fn deserialize<'d, D: Deserializer<'d>>(d: D) -> Result<Option<FeeRate>, D::Error> {
@@ -184,17 +181,14 @@ pub mod as_sat_per_vb_floor {
use core::fmt;
- use serde::{de, Deserializer, Serializer};
+ use serde::{de, Deserializer, Serialize, Serializer};
use crate::fee_rate::FeeRate;
#[inline]
#[allow(clippy::ref_option)] // API forced by serde.
pub fn serialize<S: Serializer>(f: &Option<FeeRate>, s: S) -> Result<S::Ok, S::Error> {
- match *f {
- Some(f) => s.serialize_some(&f.to_sat_per_vb_floor()),
- None => s.serialize_none(),
- }
+ f.map(FeeRate::to_sat_per_vb_floor).serialize(s)
}
pub fn deserialize<'d, D: Deserializer<'d>>(d: D) -> Result<Option<FeeRate>, D::Error> {
@@ -312,17 +306,14 @@ pub mod as_sat_per_vb_ceil {
use core::fmt;
- use serde::{de, Deserializer, Serializer};
+ use serde::{de, Deserializer, Serialize, Serializer};
use crate::fee_rate::FeeRate;
#[inline]
#[allow(clippy::ref_option)] // API forced by serde.
pub fn serialize<S: Serializer>(f: &Option<FeeRate>, s: S) -> Result<S::Ok, S::Error> {
- match *f {
- Some(f) => s.serialize_some(&f.to_sat_per_vb_ceil()),
- None => s.serialize_none(),
- }
+ f.map(FeeRate::to_sat_per_vb_ceil).serialize(s)
}
pub fn deserialize<'d, D: Deserializer<'d>>(d: D) -> Result<Option<FeeRate>, D::Error> {
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.