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}