rust_decimal_macros/lib.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
//!
//! A helpful macro for instantiating `Decimal` numbers.
//!
//! By default, this requires `rust_decimal` to be available at the project root. e.g. the macro
//! will effectively produce:
//!
//! ```ignore
//! ::rust_decimal::Decimal::from_parts(12345, 0, 0, false, 4)
//! ```
//!
//! While this is convenient for most use cases, it is sometimes not desired behavior when looking
//! to reexport the library. Consequently, this behavior can be modified by enabling the feature
//! `reexportable`. When this feature is enabled, the macro will instead reproduce the functional
//! equivalent of:
//!
//! ```ignore
//! Decimal::from_parts(12345, 0, 0, false, 4)
//! ```
//!
//! # Examples
//!
//! ```rust
//! use rust_decimal_macros::dec;
//!
//! // If the reexportable feature is enabled, `Decimal` needs to be in scope
//! #[cfg(feature = "reexportable")]
//! use rust_decimal::Decimal;
//!
//! let number = dec!(1.2345);
//! assert_eq!("1.2345", number.to_string());
//! let number = dec!(-5.4321);
//! assert_eq!("-5.4321", number.to_string());
//! ```
//!
use proc_macro::TokenStream;
use quote::quote;
use rust_decimal::Decimal;
/// Convenience function for creating decimal numbers
///
/// # Example
///
/// ```rust
/// use rust_decimal_macros::dec;
///
/// // If the reexportable feature is enabled, `Decimal` needs to be in scope
/// #[cfg(feature = "reexportable")]
/// use rust_decimal::Decimal;
///
/// let number = dec!(1.2345);
/// assert_eq!("1.2345", number.to_string());
/// let number = dec!(-5.4321);
/// assert_eq!("-5.4321", number.to_string());
/// ```
#[proc_macro]
pub fn dec(input: TokenStream) -> TokenStream {
let mut source = input.to_string();
// If it starts with `- ` then get rid of the extra space
// to_string will put a space between tokens
if source.starts_with("- ") {
source.remove(1);
}
let decimal = if source.contains('e') || source.contains('E') {
match Decimal::from_scientific(&source[..]) {
Ok(d) => d,
Err(e) => panic!("{}", e),
}
} else {
match Decimal::from_str_exact(&source[..]) {
Ok(d) => d,
Err(e) => panic!("{}", e),
}
};
let unpacked = decimal.unpack();
expand(
unpacked.lo,
unpacked.mid,
unpacked.hi,
unpacked.negative,
unpacked.scale,
)
}
#[cfg(not(feature = "reexportable"))]
fn expand(lo: u32, mid: u32, hi: u32, negative: bool, scale: u32) -> TokenStream {
let expanded = quote! {
::rust_decimal::Decimal::from_parts(#lo, #mid, #hi, #negative, #scale)
};
expanded.into()
}
#[cfg(feature = "reexportable")]
fn expand(lo: u32, mid: u32, hi: u32, negative: bool, scale: u32) -> TokenStream {
let expanded = quote! {
Decimal::from_parts(#lo, #mid, #hi, #negative, #scale)
};
expanded.into()
}