1#![doc(html_root_url = "https://docs.rs/postgres_money/0.4.1")]
2mod error;
19mod parser;
20
21#[cfg(feature = "sql")]
22mod sql_impl;
23
24use error::Error;
25use std::ops::{Add, Div, Mul, Sub};
26use std::{fmt, str};
27
28#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
30#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
31pub struct Money(Inner);
32type Inner = i64;
33
34impl Money {
35 const MIN_INNER: Inner = std::i64::MIN;
36 const MAX_INNER: Inner = std::i64::MAX;
37
38 pub const fn min() -> Money {
40 Money(Money::MIN_INNER)
41 }
42
43 pub const fn max() -> Money {
45 Money(Money::MAX_INNER)
46 }
47
48 pub const fn none() -> Money {
50 Money(0)
51 }
52
53 pub const fn inner(&self) -> Inner {
55 self.0
56 }
57
58 fn dollars(&self) -> String {
59 format!("{}", (self.0 / 100).abs())
60 }
61
62 fn cents(&self) -> String {
63 let n = (self.0 % 100).abs();
64 let mut zero_pad = "";
65 if n < 10 {
66 zero_pad = "0"
67 }
68 format!("{}{}", zero_pad, n)
69 }
70
71 fn sign(&self) -> &str {
72 if self.inner() < 0 {
73 "-"
74 } else {
75 ""
76 }
77 }
78}
79
80impl fmt::Debug for Money {
81 #[inline]
82 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83 write!(f, "{}", self)
84 }
85}
86
87impl fmt::Display for Money {
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 write!(f, "{}${}.{}", self.sign(), self.dollars(), self.cents())
90 }
91}
92
93impl str::FromStr for Money {
94 type Err = Error;
95
96 fn from_str(money_str: &str) -> Result<Self, Self::Err> {
97 Money::parse_str(money_str)
98 }
99}
100
101impl Default for Money {
102 #[inline]
103 fn default() -> Self {
104 Money::none()
105 }
106}
107
108macro_rules! derive_op_trait_from_inner {
109 (impl $imp:ident, $method:ident) => {
110 impl $imp for Money {
111 type Output = Self;
112
113 fn $method(self, rhs: Money) -> Self::Output {
114 Money(self.inner().$method(rhs.inner()))
115 }
116 }
117 };
118}
119
120macro_rules! derive_trait_for_money_with_type {
121 (impl $imp:ident with $t:ty, $method:ident) => {
122 impl $imp<$t> for Money {
123 type Output = Self;
124
125 fn $method(self, rhs: $t) -> Self::Output {
126 Money(self.inner().$method(rhs as i64))
127 }
128 }
129 };
130}
131
132macro_rules! derive_trait_for_type_with_money {
133 (impl $imp:ident with $t:ty, $method:ident) => {
134 impl $imp<Money> for $t {
135 type Output = Money;
136
137 fn $method(self, rhs: Money) -> Self::Output {
138 Money((self as i64).$method(rhs.inner()))
139 }
140 }
141 };
142}
143
144macro_rules! add_mul_impl {
145 ($($t:ty)+) => ($(
146 derive_trait_for_money_with_type! { impl Mul with $t, mul }
147 derive_trait_for_type_with_money! { impl Mul with $t, mul }
148 )+)
149}
150
151macro_rules! add_div_impl {
152 ($($t:ty)+) => ($(
153 derive_trait_for_money_with_type! { impl Div with $t, div }
154 )+)
155}
156
157derive_op_trait_from_inner!(impl Add, add);
158derive_op_trait_from_inner!(impl Sub, sub);
159add_mul_impl! { i64 i32 i16 i8 u32 u16 u8 f64 f32 }
160add_div_impl! { i64 i32 i16 i8 u32 u16 u8 }
161
162impl Div<f64> for Money {
163 type Output = Self;
164
165 fn div(self, rhs: f64) -> Self::Output {
166 let inner = self.inner() as f64 / rhs;
167 Money(inner.round() as i64)
168 }
169}
170
171impl Div<f32> for Money {
172 type Output = Self;
173
174 fn div(self, rhs: f32) -> Self::Output {
175 let inner = self.inner() as f32 / rhs;
176 Money(inner.round() as i64)
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use crate::Money;
183
184 macro_rules! gen_mul_int_tests {
185 ($t:ty, $success:ident, $success_reversed:ident, $of_max:ident, $of_min:ident) => {
186 #[test]
187 fn $success() {
188 assert_eq!(Money(7) * 3 as $t, Money(21))
189 }
190
191 #[test]
192 fn $success_reversed() {
193 assert_eq!(3 as $t * Money(7), Money(21))
194 }
195
196 #[test]
197 #[should_panic]
198 #[allow(unused_must_use)]
199 fn $of_max() {
200 Money::max() * 100 as $t;
201 }
202
203 #[test]
204 #[should_panic]
205 #[allow(unused_must_use)]
206 fn $of_min() {
207 Money::min() * 100 as $t;
208 }
209 };
210 }
211
212 macro_rules! gen_div_int_tests {
213 ($t:ty, $success:ident, $truncates:ident) => {
214 #[test]
215 fn $success() {
216 assert_eq!(Money(21) / 3 as $t, Money(7))
217 }
218
219 #[test]
220 fn $truncates() {
221 assert_eq!(Money(21) / 2 as $t, Money(10))
222 }
223 };
224 }
225
226 #[test]
227 fn test_money_default() {
228 let default_money = Money::default();
229 let nil_money = Money::none();
230
231 assert_eq!(default_money, nil_money);
232 }
233
234 gen_mul_int_tests! {
235 i64,
236 test_mul_success_i64,
237 test_mul_success_reversed_i64,
238 test_mul_fail_overflow_max_i64,
239 test_mul_fail_overflow_min_i64
240 }
241
242 gen_mul_int_tests! {
243 i32,
244 test_mul_success_i32,
245 test_mul_success_reversed_i32,
246 test_mul_fail_overflow_max_i32,
247 test_mul_fail_overflow_min_i32
248 }
249
250 gen_mul_int_tests! {
251 i16,
252 test_mul_success_i16,
253 test_mul_success_reversed_i16,
254 test_mul_fail_overflow_max_i16,
255 test_mul_fail_overflow_min_i16
256 }
257
258 gen_mul_int_tests! {
259 i8,
260 test_mul_success_i8,
261 test_mul_success_reversed_i8,
262 test_mul_fail_overflow_max_i8,
263 test_mul_fail_overflow_min_i8
264 }
265
266 gen_mul_int_tests! {
267 u32,
268 test_mul_success_u32,
269 test_mul_success_reversed_u32,
270 test_mul_fail_overflow_max_u32,
271 test_mul_fail_overflow_min_u32
272 }
273
274 gen_mul_int_tests! {
275 u16,
276 test_mul_success_u16,
277 test_mul_success_reversed_u16,
278 test_mul_fail_overflow_max_u16,
279 test_mul_fail_overflow_min_u16
280 }
281
282 gen_mul_int_tests! {
283 u8,
284 test_mul_success_u8,
285 test_mul_success_reversed_u8,
286 test_mul_fail_overflow_max_u8,
287 test_mul_fail_overflow_min_u8
288 }
289
290 gen_div_int_tests! {
291 i64,
292 test_div_success_i64,
293 test_div_truncates_i64
294 }
295
296 gen_div_int_tests! {
297 i32,
298 test_div_success_i32,
299 test_div_truncates_i32
300 }
301
302 gen_div_int_tests! {
303 i16,
304 test_div_success_i16,
305 test_div_truncates_i16
306 }
307
308 gen_div_int_tests! {
309 i8,
310 test_div_success_i8,
311 test_div_truncates_i8
312 }
313
314 gen_div_int_tests! {
315 u32,
316 test_div_success_u32,
317 test_div_truncates_u32
318 }
319
320 gen_div_int_tests! {
321 u16,
322 test_div_success_u16,
323 test_div_truncates_u16
324 }
325
326 gen_div_int_tests! {
327 u8,
328 test_div_success_u8,
329 test_div_truncates_u8
330 }
331
332 #[test]
333 fn test_addition_success() {
334 assert_eq!(Money(1) + Money(1), Money(2))
335 }
336
337 #[test]
338 #[should_panic]
339 #[allow(unused_must_use)]
340 fn test_addition_failure_overflow_max() {
341 Money::max() + Money(1);
342 }
343
344 #[test]
345 #[should_panic]
346 #[allow(unused_must_use)]
347 fn test_addition_failure_overflow_min() {
348 Money::min() + Money(-1);
349 }
350
351 #[test]
352 fn test_subtraction_success() {
353 assert_eq!(Money(2) - Money(1), Money(1))
354 }
355
356 #[test]
357 #[should_panic]
358 #[allow(unused_must_use)]
359 fn test_subtraction_failure_overflow_max() {
360 Money::max() - Money(-1);
361 }
362
363 #[test]
364 #[should_panic]
365 #[allow(unused_must_use)]
366 fn test_subtraction_failure_overflow_min() {
367 Money::min() - Money(1);
368 }
369
370 #[test]
371 fn test_money_multiply_f64() {
372 assert_eq!(Money(12300) * 2_f64, Money(24600))
373 }
374
375 #[test]
376 fn test_f64_multiply_money() {
377 assert_eq!(2_f64 * Money(12300), Money(24600))
378 }
379
380 #[test]
381 fn test_money_div_f64() {
382 assert_eq!(Money(12300) / 2_f64, Money(6150))
383 }
384
385 #[test]
386 fn test_money_multiply_f32() {
387 assert_eq!(Money(12300) * 2_f32, Money(24600))
388 }
389
390 #[test]
391 fn test_f32_multiply_money() {
392 assert_eq!(2_f32 * Money(12300), Money(24600))
393 }
394
395 #[test]
396 fn test_money_div_f32() {
397 assert_eq!(Money(12300) / 2_f32, Money(6150))
398 }
399
400 #[test]
402 fn test_eq() {
403 assert_eq!(Money(12300), Money(12300))
404 }
405
406 #[test]
407 fn test_not_eq() {
408 assert_ne!(Money(12300), Money(12400))
409 }
410
411 #[test]
412 fn test_lt_eq() {
413 assert!(Money(12300) <= Money(12300))
414 }
415
416 #[test]
417 fn test_gt_eq() {
418 assert!(Money(12300) >= Money(12300))
419 }
420
421 #[test]
422 fn test_lt() {
423 assert!(Money(12300) < Money(12400))
424 }
425
426 #[test]
427 fn test_gt() {
428 assert!(Money(12300) > Money(12200))
429 }
430
431 #[test]
432 fn test_eq_inverse() {
433 assert!(Money(12300) != Money(12301))
434 }
435
436 #[test]
437 fn test_not_eq_inverse() {
438 assert!(Money(12300) == Money(12300))
439 }
440
441 #[test]
442 fn test_lt_eq_inverse() {
443 assert!(Money(12300) > Money(12299))
444 }
445
446 #[test]
447 fn test_gt_eq_inverse() {
448 assert!(Money(12300) < Money(12301))
449 }
450
451 #[test]
452 fn test_lt_inverse() {
453 assert!(Money(12300) >= Money(12200))
454 }
455
456 #[test]
457 fn test_gt_inverse() {
458 assert!(Money(12300) <= Money(12400))
459 }
460
461 #[test]
463 fn test_rounded_division_f64() {
464 assert_eq!(Money(87808) / 11.0_f64, Money(7983))
465 }
466
467 #[test]
468 fn test_rounded_division_f32() {
469 assert_eq!(Money(87808) / 11.0_f32, Money(7983))
470 }
471
472 #[test]
473 fn test_truncated_division_i64() {
474 assert_eq!(Money(87808) / 11_i64, Money(7982))
475 }
476
477 #[test]
478 fn test_truncated_division_i32() {
479 assert_eq!(Money(87808) / 11_i32, Money(7982))
480 }
481
482 #[test]
483 fn test_truncated_division_i16() {
484 assert_eq!(Money(87808) / 11_i16, Money(7982))
485 }
486
487 #[test]
488 fn test_truncated_division_i8() {
489 assert_eq!(Money(87808) / 11_i8, Money(7982))
490 }
491
492 #[test]
494 fn test_precision_loss_i64() {
495 assert_eq!(
496 Money(9000000000000009900) / 10_i64,
497 Money(900000000000000990)
498 )
499 }
500
501 #[test]
502 fn test_precision_loss_i32() {
503 assert_eq!(
504 Money(9000000000000009900) / 10_i32,
505 Money(900000000000000990)
506 )
507 }
508
509 #[test]
510 fn test_precision_loss_i16() {
511 assert_eq!(
512 Money(9000000000000009900) / 10_i16,
513 Money(900000000000000990)
514 )
515 }
516
517 #[test]
518 fn test_precision_loss_i8() {
519 assert_eq!(
520 Money(9000000000000009900) / 10_i8,
521 Money(900000000000000990)
522 )
523 }
524}