diesel/pg/types/
money.rs

1//! Support for Money values under PostgreSQL.
2
3use std::io::prelude::*;
4use std::ops::{Add, AddAssign, Sub, SubAssign};
5
6use deserialize::{self, FromSql};
7use pg::Pg;
8use serialize::{self, Output, ToSql};
9use sql_types::{BigInt, Money};
10
11/// Money is represented in Postgres as a 64 bit signed integer.  This struct is a dumb wrapper
12/// type, meant only to indicate the integer's meaning.  The fractional precision of the value is
13/// determined by the [`lc_monetary` setting of the database](https://www.postgresql.org/docs/9.6/static/datatype-money.html).
14/// This struct is re-exported as `Cents` as a convenient and conventional expression of a typical
15/// unit of 1/100th of currency. For other names or precisions, users might consider a differently
16/// named `use` of the `PgMoney` struct.
17///
18/// ```rust
19/// use diesel::data_types::PgMoney as Pence; // 1/100th unit of Pound
20/// use diesel::data_types::PgMoney as Fils;  // 1/1000th unit of Dinar
21/// ```
22#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, FromSqlRow, AsExpression)]
23#[sql_type = "Money"]
24pub struct PgMoney(pub i64);
25
26impl FromSql<Money, Pg> for PgMoney {
27    fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result<Self> {
28        FromSql::<BigInt, Pg>::from_sql(bytes).map(PgMoney)
29    }
30}
31
32impl ToSql<Money, Pg> for PgMoney {
33    fn to_sql<W: Write>(&self, out: &mut Output<W, Pg>) -> serialize::Result {
34        ToSql::<BigInt, Pg>::to_sql(&self.0, out)
35    }
36}
37
38impl Add for PgMoney {
39    type Output = Self;
40    /// # Panics
41    ///
42    /// Performs a checked addition, and will `panic!` on overflow in both `debug` and `release`.
43    fn add(self, rhs: PgMoney) -> Self::Output {
44        self.0
45            .checked_add(rhs.0)
46            .map(PgMoney)
47            .expect("overflow adding money amounts")
48    }
49}
50
51impl AddAssign for PgMoney {
52    /// # Panics
53    ///
54    /// Performs a checked addition, and will `panic!` on overflow in both `debug` and `release`.
55    fn add_assign(&mut self, rhs: PgMoney) {
56        self.0 = self
57            .0
58            .checked_add(rhs.0)
59            .expect("overflow adding money amounts")
60    }
61}
62
63impl Sub for PgMoney {
64    type Output = Self;
65    /// # Panics
66    ///
67    /// Performs a checked subtraction, and will `panic!` on underflow in both `debug` and `release`.
68    fn sub(self, rhs: PgMoney) -> Self::Output {
69        self.0
70            .checked_sub(rhs.0)
71            .map(PgMoney)
72            .expect("underflow subtracting money amounts")
73    }
74}
75
76impl SubAssign for PgMoney {
77    /// # Panics
78    ///
79    /// Performs a checked subtraction, and will `panic!` on underflow in both `debug` and `release`.
80    fn sub_assign(&mut self, rhs: PgMoney) {
81        self.0 = self
82            .0
83            .checked_sub(rhs.0)
84            .expect("underflow subtracting money amounts")
85    }
86}
87
88#[cfg(feature = "quickcheck")]
89mod quickcheck_impls {
90    extern crate quickcheck;
91
92    use self::quickcheck::{Arbitrary, Gen};
93    use super::PgMoney;
94
95    impl Arbitrary for PgMoney {
96        fn arbitrary<G: Gen>(g: &mut G) -> Self {
97            PgMoney(i64::arbitrary(g))
98        }
99    }
100}
101
102#[test]
103fn add_money() {
104    let c1 = PgMoney(123);
105    let c2 = PgMoney(456);
106    assert_eq!(PgMoney(579), c1 + c2);
107}
108
109#[test]
110fn add_assign_money() {
111    let mut c1 = PgMoney(123);
112    c1 += PgMoney(456);
113    assert_eq!(PgMoney(579), c1);
114}
115
116#[test]
117#[should_panic(expected = "overflow adding money amounts")]
118fn add_money_overflow() {
119    let c1 = PgMoney(::std::i64::MAX);
120    let c2 = PgMoney(1);
121    let _overflow = c1 + c2;
122}
123
124#[test]
125#[should_panic(expected = "overflow adding money amounts")]
126fn add_assign_money_overflow() {
127    let mut c1 = PgMoney(::std::i64::MAX);
128    c1 += PgMoney(1);
129}
130
131#[test]
132fn sub_money() {
133    let c1 = PgMoney(123);
134    let c2 = PgMoney(456);
135    assert_eq!(PgMoney(-333), c1 - c2);
136}
137
138#[test]
139fn sub_assign_money() {
140    let mut c1 = PgMoney(123);
141    c1 -= PgMoney(456);
142    assert_eq!(PgMoney(-333), c1);
143}
144
145#[test]
146#[should_panic(expected = "underflow subtracting money amounts")]
147fn sub_money_underflow() {
148    let c1 = PgMoney(::std::i64::MIN);
149    let c2 = PgMoney(1);
150    let _underflow = c1 - c2;
151}
152
153#[test]
154#[should_panic(expected = "underflow subtracting money amounts")]
155fn sub_assign_money_underflow() {
156    let mut c1 = PgMoney(::std::i64::MIN);
157    c1 -= PgMoney(1);
158}