mod str;
use proc_macro::TokenStream;
use quote::quote;
use syn::{
parse::{Parse, ParseStream},
parse_macro_input, Expr, Ident, LitInt, Result, Token,
};
#[proc_macro]
pub fn dec(input: TokenStream) -> TokenStream {
let dec_input = parse_macro_input!(input as DecInputParser);
let value = dec_input.value.as_str();
let result = if let Some(radix) = dec_input.radix {
str::parse_decimal_with_radix(value, dec_input.exp.unwrap_or_default(), radix)
} else {
str::parse_decimal(value, dec_input.exp.unwrap_or_default())
};
let unpacked = match result {
Ok(d) => d,
Err(e) => panic!("{}", e),
};
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()
}
struct DecInputParser {
radix: Option<u32>,
exp: Option<i32>,
value: String,
}
impl Parse for DecInputParser {
fn parse(input: ParseStream) -> Result<Self> {
let mut radix = None;
let mut exp = None;
let mut value = None;
if !input.is_empty() {
if input.peek(Ident) {
let ident = input.parse::<Ident>()?;
let ident_str = ident.to_string();
match ident_str.as_str() {
"radix" => {
if let Some(value) = parse_radix(input)? {
radix = Some(value);
}
}
"exp" => {
if let Some(value) = parse_exp(input)? {
exp = Some(value);
}
}
_ => {
value = Some(ident_str);
}
}
} else {
let expr = input.parse::<Expr>()?;
value = Some(quote!(#expr).to_string());
}
}
while !input.is_empty() {
if input.peek(Token![,]) {
let _ = input.parse::<Token![,]>()?;
}
if input.is_empty() {
break;
}
if input.peek(Ident) {
let ident = input.parse::<Ident>()?;
let ident_str = ident.to_string();
match ident_str.as_str() {
"radix" => {
if radix.is_some() {
panic!("Duplicate radix parameter");
}
if let Some(value) = parse_radix(input)? {
radix = Some(value);
}
}
"exp" => {
if exp.is_some() {
panic!("Duplicate exp parameter");
}
if let Some(value) = parse_exp(input)? {
exp = Some(value);
}
}
_ => {
if value.is_none() {
value = Some(ident_str);
} else {
panic!("Unknown parameter or duplicate value: {}", ident_str);
}
}
}
} else {
if value.is_none() {
let expr = input.parse::<Expr>()?;
value = Some(quote!(#expr).to_string());
} else {
panic!("Duplicate value found");
}
}
}
let value = value.unwrap_or_else(|| panic!("Expected a decimal value"));
Ok(DecInputParser { radix, exp, value })
}
}
fn parse_radix(input: ParseStream) -> Result<Option<u32>> {
if input.peek(LitInt) {
let lit_int = input.parse::<LitInt>()?;
return Ok(Some(lit_int.base10_parse::<u32>()?));
}
let expr = input.parse::<Expr>()?;
match expr {
Expr::Lit(lit) => {
if let syn::Lit::Int(lit_int) = lit.lit {
return Ok(Some(lit_int.base10_parse::<u32>()?));
}
}
_ => panic!("Expected a literal integer for radix"),
}
Ok(None)
}
fn parse_exp(input: ParseStream) -> Result<Option<i32>> {
if input.peek(LitInt) {
let lit_int = input.parse::<LitInt>()?;
return Ok(Some(lit_int.base10_parse::<i32>()?));
}
let expr = input.parse::<Expr>()?;
match expr {
Expr::Lit(lit) => {
if let syn::Lit::Int(lit_int) = lit.lit {
return Ok(Some(lit_int.base10_parse::<i32>()?));
}
}
Expr::Unary(unary) => {
if let Expr::Lit(lit) = *unary.expr {
if let syn::Lit::Int(lit_int) = lit.lit {
let mut val = lit_int.base10_parse::<i32>()?;
if let syn::UnOp::Neg(_) = unary.op {
val = -val;
}
return Ok(Some(val));
}
}
}
_ => panic!("Expected a literal integer for exp"),
}
Ok(None)
}