diff --git a/sqlx-core/src/postgres/types/money.rs b/sqlx-core/src/postgres/types/money.rs index 1da24bdc2..8810664bf 100644 --- a/sqlx-core/src/postgres/types/money.rs +++ b/sqlx-core/src/postgres/types/money.rs @@ -61,6 +61,32 @@ impl PgMoney { Self(i64::from_le_bytes(buf)) } + + /// Convert a [`BigDecimal`] value into money using the correct precision + /// defined in the PostgreSQL settings. The default precision is two. + /// + /// [`BigDecimal`]: ../../types/struct.BigDecimal.html + #[cfg(feature = "bigdecimal")] + pub fn from_bigdecimal( + decimal: bigdecimal::BigDecimal, + scale: u32, + ) -> Result { + use bigdecimal::ToPrimitive; + + let multiplier = + bigdecimal::BigDecimal::new(num_bigint::BigInt::from(10i128.pow(scale)), 0); + + let cents = decimal * multiplier; + + let money = cents.to_i64().ok_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidData, + "Provided BigDecimal could not convert to i64: overflow.", + ) + })?; + + Ok(Self(money)) + } } impl Type for PgMoney { @@ -257,4 +283,11 @@ mod tests { assert_eq!(PgMoney(12345), PgMoney::from_decimal(dec, 2)); } + + #[cfg(feature = "bigdecimal")] + fn conversion_from_bigdecimal_works() { + let dec = bigdecimal::BigDecimal::new(num_bigint::BigInt::from(12345), 2); + + assert_eq!(PgMoney(12345), PgMoney::from_bigdecimal(dec, 2).unwrap()); + } }