fpdec_macros/
lib.rs

1// ---------------------------------------------------------------------------
2// Copyright:   (c) 2021 ff. Michael Amrhein (michael@adrhinum.de)
3// License:     This program is part of a larger application. For license
4//              details please read the file LICENSE.TXT provided together
5//              with the application.
6// ---------------------------------------------------------------------------
7// $Source: fpdec-macros/src/lib.rs $
8// $Revision: 2022-08-17T18:32:32+02:00 $
9
10#![doc = include_str ! ("../README.md")]
11
12use ::proc_macro::TokenStream;
13use ::quote::quote;
14use fpdec_core::{str_to_dec, ParseDecimalError, MAX_N_FRAC_DIGITS};
15
16/// Macro used to convert a number literal into a `Decimal`.
17///
18/// The literal must be in the form
19/// \[+|-]\<int\>\[.\<frac\>]\[<e|E>\[+|-]\<exp\>] or
20/// \[+|-].\<frac\>\[<e|E>\[+|-]\<exp\>].
21///
22/// The resulting number of fractional digits is determined by the number of
23/// digits in the fractional part of the literal minus the value of the signed
24/// exponent.
25///
26/// The resulting value must not exceed the limits given by the internal
27/// representaion of `Decimal`:
28/// * The coefficient must fit into an `i128`.
29/// * The number of fractional digits must not exceed the constant
30///   `MAX_N_FRAC_DIGITS`.
31///
32/// # Panics
33///
34/// The macro panics if the conditions listed above are not met!
35///
36/// # Examples
37///
38/// ```ignore
39/// let d = Dec!(17.5);
40/// assert_eq!(d.to_string(), "17.5");
41///
42/// let d = Dec!(-170.5e-2);
43/// assert_eq!(d.to_string(), "-1.705");
44/// ```
45#[allow(non_snake_case)]
46#[proc_macro]
47pub fn Dec(input: TokenStream) -> TokenStream {
48    let mut src = input.to_string();
49    // "-" and "+" get separated by a blank => remove it
50    if src.starts_with("- ") || src.starts_with("+ ") {
51        src.remove(1);
52    }
53    match str_to_dec(&src) {
54        Err(e) => panic!("{}", e),
55        Ok((mut coeff, mut exponent)) => {
56            if -exponent > (MAX_N_FRAC_DIGITS as isize) {
57                panic!("{}", ParseDecimalError::FracDigitLimitExceeded)
58            }
59            if exponent > 38 {
60                // 10 ^ 39 > int128::MAX
61                panic!("{}", ParseDecimalError::InternalOverflow);
62            }
63            if exponent > 0 {
64                match coeff.checked_mul(10i128.pow(exponent as u32)) {
65                    None => panic!("{}", ParseDecimalError::InternalOverflow),
66                    Some(val) => {
67                        coeff = val;
68                    }
69                }
70                exponent = 0;
71            }
72            let n_frac_digits = -exponent as u8;
73            quote!(
74                Decimal::new_raw(#coeff, #n_frac_digits)
75            )
76            .into()
77        }
78    }
79}