Skip to main content

no_mangle_if_debug/
lib.rs

1//! Will add `#[no_mangle]` to the item it is applied but only in debug mode.
2//!
3//! This is useful for use with [hot-lib-reloader](https://crates.io/crates/hot-lib-reloader) to conditionally expose library functions to the lib reloader only in debug mode.
4//! In release mode where a build is to be expected fully static, no additional penalty is paid.
5//!
6//! ```xxx
7//! #[no_mangle_if_debug]
8//! fn func() {}
9//! ```
10//!
11//! will expand to
12//!
13//! ```xxx
14//! #[cfg(debug_assertions)]
15//! #[no_mangle]
16//! fn func() {}
17//!
18//! #[cfg(not(debug_assertions))]
19//! fn func() {}
20//! ```
21
22use proc_macro2::TokenStream;
23use quote::{quote, ToTokens};
24use syn::{parse_macro_input, token};
25
26/// See package doc.
27#[proc_macro_attribute]
28pub fn no_mangle_if_debug(
29    _args: proc_macro::TokenStream,
30    item: proc_macro::TokenStream,
31) -> proc_macro::TokenStream {
32    let mut debug_item = parse_macro_input!(item as syn::ItemFn);
33    let mut release_item = debug_item.clone();
34
35    debug_item.attrs.push(create_attribute(
36        "cfg",
37        quote! { (debug_assertions) }.into_token_stream(),
38    ));
39    debug_item
40        .attrs
41        .push(create_attribute("no_mangle", Default::default()));
42
43    release_item.attrs.push(create_attribute(
44        "cfg",
45        quote! { (not(debug_assertions)) }.into_token_stream(),
46    ));
47
48    (quote! {
49        #debug_item
50        #release_item
51    })
52    .into()
53}
54
55fn create_attribute(ident: &str, tokens: TokenStream) -> syn::Attribute {
56    let span = proc_macro2::Span::call_site();
57    syn::Attribute {
58        style: syn::AttrStyle::Outer,
59        pound_token: token::Pound { spans: [span] },
60        bracket_token: token::Bracket::default(),
61        path: syn::Path {
62            leading_colon: None,
63            segments: [syn::PathSegment {
64                ident: syn::Ident::new(ident, span),
65                arguments: syn::PathArguments::None,
66            }]
67            .into_iter()
68            .collect(),
69        },
70        tokens,
71    }
72}