diesel/pg/types/
money.rs

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