inline_tweak_derive/
lib.rs

1extern crate proc_macro;
2use proc_macro::TokenStream;
3use proc_macro2::{Ident, Span};
4use quote::ToTokens;
5use syn::punctuated::Punctuated;
6use syn::visit_mut::VisitMut;
7use syn::{
8    parse_macro_input, Attribute, Expr, ExprBreak, ExprConst, ExprMacro, ItemConst, ItemStatic,
9    Lit, LitInt, LitStr, Macro, MacroDelimiter, Path, PathSegment, Token, Type,
10};
11
12struct LiteralReplacer {
13    nth: usize,
14    fname: Ident,
15    release_tweak: bool,
16}
17
18impl LiteralReplacer {
19    fn replace(&mut self, i: &mut Expr) {
20        let expr = std::mem::replace(
21            i,
22            Expr::Break(ExprBreak {
23                attrs: vec![],
24                break_token: Default::default(),
25                label: None,
26                expr: None,
27            }),
28        );
29
30        *i = Expr::Macro(ExprMacro {
31            attrs: vec![],
32            mac: Macro {
33                path: Path {
34                    segments: [
35                        PathSegment::from(Ident::new("inline_tweak", Span::call_site())),
36                        PathSegment::from(Ident::new(
37                            if self.release_tweak {
38                                "derive_release_tweak"
39                            } else {
40                                "derive_tweak"
41                            },
42                            Span::call_site(),
43                        )),
44                    ]
45                    .into_iter()
46                    .collect(),
47                    leading_colon: Some(Default::default()),
48                },
49                bang_token: Default::default(),
50                delimiter: MacroDelimiter::Paren(Default::default()),
51                tokens: [
52                    expr,
53                    Expr::Lit(syn::ExprLit {
54                        attrs: vec![],
55                        lit: Lit::Str(LitStr::new(&self.fname.to_string(), Span::call_site())),
56                    }),
57                    Expr::Lit(syn::ExprLit {
58                        attrs: vec![],
59                        lit: Lit::Int(LitInt::new(&self.nth.to_string(), Span::call_site())),
60                    }),
61                ]
62                .into_iter()
63                .collect::<Punctuated<Expr, Token![,]>>()
64                .into_token_stream(),
65            },
66        });
67
68        self.nth += 1;
69    }
70}
71
72impl VisitMut for LiteralReplacer {
73    fn visit_expr_mut(&mut self, i: &mut Expr) {
74        match *i {
75            Expr::Lit(syn::ExprLit {
76                lit: Lit::Char(_) | Lit::Int(_) | Lit::Float(_) | Lit::Bool(_) | Lit::Str(_),
77                ..
78            }) => {
79                self.replace(i);
80            }
81            Expr::Unary(syn::ExprUnary {
82                op: syn::UnOp::Neg(_),
83                ref expr,
84                ..
85            }) if matches!(
86                &**expr,
87                Expr::Lit(syn::ExprLit {
88                    lit: Lit::Int(_) | Lit::Float(_),
89                    ..
90                })
91            ) =>
92            {
93                self.replace(i);
94            }
95            _ => syn::visit_mut::visit_expr_mut(self, i),
96        }
97    }
98
99    fn visit_expr_const_mut(&mut self, _: &mut ExprConst) {}
100
101    fn visit_item_const_mut(&mut self, _: &mut ItemConst) {}
102
103    fn visit_item_static_mut(&mut self, _: &mut ItemStatic) {}
104
105    fn visit_type_mut(&mut self, _: &mut Type) {}
106
107    fn visit_attribute_mut(&mut self, _: &mut Attribute) {}
108}
109
110/// Makes all the number/bool/char literals in a function tweakable.  
111/// Doesn't apply to literals in macros as they cannot be replaced by expressions reliably. (e.g in calls to println!)
112///
113/// # Examples
114///
115/// ```rust
116/// # use std::time::Duration;
117/// #[inline_tweak::tweak_fn]
118/// fn main() {
119///     loop {
120///         let v = 1.0; // Try changing this value!
121///         println!("{}", v);
122///         std::thread::sleep(Duration::from_millis(200)); // or even this value :)
123///     }
124/// }
125/// ```
126#[proc_macro_attribute]
127pub fn tweak_fn(_attr: TokenStream, item: TokenStream) -> TokenStream {
128    do_fn(item, false)
129}
130
131/// Makes all the number/bool/char literals in a function tweakable.  
132/// Doesn't apply to literals in macros as they cannot be replaced by expressions reliably. (e.g in calls to println!)
133///
134/// # Examples
135///
136/// ```rust
137/// # use std::time::Duration;
138/// #[inline_tweak::tweak_fn]
139/// fn main() {
140///     loop {
141///         let v = 1.0; // Try changing this value!
142///         println!("{}", v);
143///         std::thread::sleep(Duration::from_millis(200)); // or even this value :)
144///     }
145/// }
146/// ```#[cfg(feature = "release_tweak")]
147#[proc_macro_attribute]
148pub fn release_tweak_fn(_attr: TokenStream, item: TokenStream) -> TokenStream {
149    do_fn(item, true)
150}
151
152fn do_fn(item: TokenStream, release_tweak: bool) -> TokenStream {
153    let mut v: syn::ItemFn = parse_macro_input!(item as syn::ItemFn);
154
155    let fname = v.sig.ident.clone();
156
157    LiteralReplacer {
158        nth: 0,
159        fname,
160        release_tweak,
161    }
162    .visit_item_fn_mut(&mut v);
163
164    v.into_token_stream().into()
165}