use std::str::FromStr;
use bigdecimal::BigDecimal;
use num_bigint::BigInt;
use serde::Deserialize;
use serde::Deserializer;
use serde::Serialize;
use super::big_decimal_payload_visitor::BigDecimalPayloadVisitor;
use crate::wire::is_valid_big_decimal_scale;
#[derive(Serialize)]
pub(in crate::wire) struct BigDecimalPayload {
pub(in crate::wire) coefficient: String,
pub(in crate::wire) scale: i64,
}
impl<'de> Deserialize<'de> for BigDecimalPayload {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let allow_sequence = !deserializer.is_human_readable();
deserializer.deserialize_struct(
"BigDecimalPayload",
&["coefficient", "scale"],
BigDecimalPayloadVisitor::new(allow_sequence),
)
}
}
impl TryFrom<&BigDecimal> for BigDecimalPayload {
type Error = &'static str;
#[inline]
fn try_from(value: &BigDecimal) -> Result<Self, Self::Error> {
let (coefficient, scale) = value.as_bigint_and_scale();
if !is_valid_big_decimal_scale(scale) {
return Err("decimal scale exceeds the V1 maximum absolute scale");
}
Ok(Self {
coefficient: coefficient.to_string(),
scale,
})
}
}
impl TryFrom<BigDecimalPayload> for BigDecimal {
type Error = &'static str;
fn try_from(value: BigDecimalPayload) -> Result<Self, Self::Error> {
if !is_valid_big_decimal_scale(value.scale) {
return Err("decimal scale exceeds the V1 maximum absolute scale");
}
let coefficient =
BigInt::from_str(&value.coefficient).map_err(|_| "invalid decimal coefficient")?;
if coefficient.to_string() != value.coefficient {
return Err("non-canonical decimal coefficient");
}
Ok(Self::new(coefficient, value.scale))
}
}