use sqlx_core::error::BoxDynError;
use sqlx_core::postgres::PgArgumentBuffer;
use self::bytes::Buf;
extern crate bytes;
#[derive(Debug, PartialEq, Eq)]
pub(crate) enum PgNumeric {
NotANumber,
Number {
sign: PgNumericSign,
digits: Vec<i16>,
weight: i16,
scale: i16,
},
}
const SIGN_POS: u16 = 0x0000;
const SIGN_NEG: u16 = 0x4000;
const SIGN_NAN: u16 = 0xC000;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u16)]
pub(crate) enum PgNumericSign {
Positive = SIGN_POS,
Negative = SIGN_NEG,
}
impl PgNumericSign {
fn try_from_u16(val: u16) -> Result<Self, BoxDynError> {
match val {
SIGN_POS => Ok(PgNumericSign::Positive),
SIGN_NEG => Ok(PgNumericSign::Negative),
SIGN_NAN => unreachable!("sign value for NaN passed to PgNumericSign"),
_ => Err(format!("invalid value for PgNumericSign: {:#04X}", val).into()),
}
}
}
impl PgNumeric {
pub(crate) fn decode(mut buf: &[u8]) -> Result<Self, BoxDynError> {
let num_digits = buf.get_u16();
let weight = buf.get_i16();
let sign = buf.get_u16();
let scale = buf.get_i16();
if sign == SIGN_NAN {
Ok(PgNumeric::NotANumber)
} else {
let digits: Vec<_> = (0..num_digits).map(|_| buf.get_i16()).collect::<_>();
Ok(PgNumeric::Number {
sign: PgNumericSign::try_from_u16(sign)?,
scale,
weight,
digits,
})
}
}
pub(crate) fn encode(&self, buf: &mut PgArgumentBuffer) {
match *self {
PgNumeric::Number {
ref digits,
sign,
scale,
weight,
} => {
let digits_len: i16 = digits
.len()
.try_into()
.expect("PgNumeric.digits.len() should not overflow i16");
buf.extend(&digits_len.to_be_bytes());
buf.extend(&weight.to_be_bytes());
buf.extend(&(sign as i16).to_be_bytes());
buf.extend(&scale.to_be_bytes());
for digit in digits {
debug_assert!(*digit < 10000, "PgNumeric digits must be in base-10000");
buf.extend(&digit.to_be_bytes());
}
}
PgNumeric::NotANumber => {
buf.extend(&0_i16.to_be_bytes());
buf.extend(&0_i16.to_be_bytes());
buf.extend(&SIGN_NAN.to_be_bytes());
buf.extend(&0_i16.to_be_bytes());
}
}
}
}