extern crate proc_macro;
use proc_macro::TokenStream;
use syn::visit_mut::{visit_expr_mut, VisitMut};
use syn::{parse_macro_input, Expr, ExprLit, Item, Lit};
use quote::quote;
struct NumericLiteralVisitor<'a> {
pub placeholder: &'a str,
pub float_replacement: &'a Expr,
pub int_replacement: &'a Expr,
}
struct FloatLiteralVisitor<'a> {
pub placeholder: &'a str,
pub replacement: &'a Expr,
}
struct IntLiteralVisitor<'a> {
pub placeholder: &'a str,
pub replacement: &'a Expr,
}
fn replace_literal(expr: &mut Expr, placeholder: &str, literal: &ExprLit) {
let mut replacer = ReplacementExpressionVisitor {
placeholder,
literal,
};
replacer.visit_expr_mut(expr);
}
impl<'a> VisitMut for FloatLiteralVisitor<'a> {
fn visit_expr_mut(&mut self, expr: &mut Expr) {
if let Expr::Lit(lit_expr) = expr {
if let Lit::Float(_) = lit_expr.lit {
let mut adapted_replacement = self.replacement.clone();
replace_literal(&mut adapted_replacement, self.placeholder, lit_expr);
*expr = adapted_replacement;
return;
}
}
visit_expr_mut(self, expr)
}
}
impl<'a> VisitMut for IntLiteralVisitor<'a> {
fn visit_expr_mut(&mut self, expr: &mut Expr) {
if let Expr::Lit(lit_expr) = expr {
if let Lit::Int(_) = lit_expr.lit {
let mut adapted_replacement = self.replacement.clone();
replace_literal(&mut adapted_replacement, self.placeholder, lit_expr);
*expr = adapted_replacement;
return;
}
}
visit_expr_mut(self, expr)
}
}
impl<'a> VisitMut for NumericLiteralVisitor<'a> {
fn visit_expr_mut(&mut self, expr: &mut Expr) {
if let Expr::Lit(lit_expr) = expr {
match lit_expr.lit {
Lit::Int(_) => {
let mut visitor = IntLiteralVisitor {
placeholder: self.placeholder,
replacement: self.int_replacement,
};
visitor.visit_expr_mut(expr);
return;
}
Lit::Float(_) => {
let mut visitor = FloatLiteralVisitor {
placeholder: self.placeholder,
replacement: self.float_replacement,
};
visitor.visit_expr_mut(expr);
return;
}
_ => {}
}
}
visit_expr_mut(self, expr)
}
}
struct ReplacementExpressionVisitor<'a> {
pub placeholder: &'a str,
pub literal: &'a ExprLit,
}
impl<'a> VisitMut for ReplacementExpressionVisitor<'a> {
fn visit_expr_mut(&mut self, expr: &mut Expr) {
if let Expr::Path(path_expr) = expr {
if let Some(last_segment) = path_expr.path.segments.last() {
if last_segment.ident == self.placeholder {
*expr = Expr::Lit(self.literal.clone());
return;
}
}
}
visit_expr_mut(self, expr)
}
}
#[proc_macro_attribute]
pub fn replace_numeric_literals(attr: TokenStream, item: TokenStream) -> TokenStream {
let mut input = parse_macro_input!(item as Item);
let attributes_tree = parse_macro_input!(attr as Expr);
let mut replacer = NumericLiteralVisitor {
placeholder: "literal",
int_replacement: &attributes_tree,
float_replacement: &attributes_tree,
};
replacer.visit_item_mut(&mut input);
let expanded = quote! { #input };
TokenStream::from(expanded)
}
#[proc_macro_attribute]
pub fn replace_float_literals(attr: TokenStream, item: TokenStream) -> TokenStream {
let mut input = parse_macro_input!(item as Item);
let attributes_tree = parse_macro_input!(attr as Expr);
let mut replacer = FloatLiteralVisitor {
placeholder: "literal",
replacement: &attributes_tree,
};
replacer.visit_item_mut(&mut input);
let expanded = quote! { #input };
TokenStream::from(expanded)
}
#[proc_macro_attribute]
pub fn replace_int_literals(attr: TokenStream, item: TokenStream) -> TokenStream {
let mut input = parse_macro_input!(item as Item);
let attributes_tree = parse_macro_input!(attr as Expr);
let mut replacer = IntLiteralVisitor {
placeholder: "literal",
replacement: &attributes_tree,
};
replacer.visit_item_mut(&mut input);
let expanded = quote! { #input };
TokenStream::from(expanded)
}