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}