rbdc_pg/types/
money.rs

1use crate::arguments::PgArgumentBuffer;
2use crate::types::decode::Decode;
3use crate::types::encode::{Encode, IsNull};
4use crate::value::{PgValue, PgValueFormat};
5use byteorder::{BigEndian, ByteOrder};
6use rbdc::Error;
7use rbs::Value;
8use std::fmt::{Display, Formatter};
9
10/// The raw integer value sent over the wire; for locales with `frac_digits=2` (i.e. most
11/// of them), this will be the value in whole cents.
12///
13/// E.g. for `select '$123.45'::money` with a locale of `en_US` (`frac_digits=2`),
14/// this will be `12345`.
15///
16/// If the currency of your locale does not have fractional units, e.g. Yen, then this will
17/// just be the units of the currency.
18///
19/// See the type-level docs for an explanation of `locale_frac_units`.
20#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Eq, PartialEq)]
21#[serde(rename = "Money")]
22pub struct Money(pub i64);
23
24impl Display for Money {
25    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
26        write!(f, "{}", self.0)
27    }
28}
29
30impl From<Money> for Value {
31    fn from(arg: Money) -> Self {
32        Value::Ext("Money", Box::new(Value::I64(arg.0)))
33    }
34}
35
36impl Encode for Money {
37    fn encode(self, buf: &mut PgArgumentBuffer) -> Result<IsNull, Error> {
38        buf.extend(&self.0.to_be_bytes());
39        Ok(IsNull::No)
40    }
41}
42
43impl Decode for Money {
44    fn decode(value: PgValue) -> Result<Self, Error> {
45        Ok(Self({
46            match value.format() {
47                PgValueFormat::Binary => {
48                    let cents = BigEndian::read_i64(value.as_bytes()?);
49                    Ok(cents)
50                }
51                PgValueFormat::Text => Err(Error::from(
52                    "Reading a `MONEY` value in text format is not supported.",
53                )),
54            }
55        }?))
56    }
57}