rust_fixed_point_decimal_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: macros/src/lib.rs $
8// $Revision: 2021-10-22T21:53:08+02:00 $
9
10use ::proc_macro::TokenStream;
11use ::quote::quote;
12use rust_fixed_point_decimal_core::{
13    dec_repr_from_str, ParseDecimalError, MAX_PREC,
14};
15
16/// Macro used to convert a number literal into a Decimal\<P\>.
17///
18/// The literal must be in the form
19/// \[+|-]\<int\>\[.\<frac\>]\[<e|E>\[+|-]\<exp\>] or
20/// \[+|-].\<frac\>\[<e|E>\[+|-]\<exp\>].
21///
22/// P is determined by the number of fractional digits minus the value of the
23/// signed exponent. It must not exceed the constant MAX_PREC.
24///
25/// The resulting value must not exceed the limits given by Decimal::\<P\>::MIN
26/// and Decimal::\<P\>::MAX.
27///
28/// # Panics
29///
30/// The macro panics if the conditions listed above are not met!
31///
32/// # Examples
33///
34/// ```ignore
35/// let d = Dec!(17.5);
36/// assert_eq!(d.to_string(), "17.5");
37///
38/// let d = Dec!(-170.5e-2);
39/// assert_eq!(d.to_string(), "-1.705");
40/// ```
41#[allow(non_snake_case)]
42#[proc_macro]
43pub fn Dec(input: TokenStream) -> TokenStream {
44    let mut src = input.to_string();
45    // "-" and "+" get separated by a blank => remove it
46    if src.starts_with("- ") || src.starts_with("+ ") {
47        src.remove(1);
48    }
49
50    match dec_repr_from_str(&src) {
51        Err(e) => panic!("{}", e),
52        Ok((mut coeff, mut exponent)) => {
53            if -exponent > (MAX_PREC as isize) {
54                panic!("{}", ParseDecimalError::PrecLimitExceeded)
55            }
56            if exponent > 38 {
57                // 10 ^ 39 > int128::MAX
58                panic!("{}", ParseDecimalError::MaxValueExceeded);
59            }
60            if exponent > 0 {
61                match coeff.checked_mul(10i128.pow(exponent as u32)) {
62                    None => panic!("{}", ParseDecimalError::MaxValueExceeded),
63                    Some(val) => {
64                        coeff = val;
65                    }
66                }
67                exponent = 0;
68            }
69            let prec = -exponent as u8;
70            quote!(
71                rust_fixed_point_decimal::Decimal::<#prec>
72                ::new_raw(#coeff)
73            )
74            .into()
75        }
76    }
77}