barexp_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::parse_macro_input;
4use syn::Item;
5use std::collections::HashMap;
6use std::sync::Mutex;
7use once_cell::sync::Lazy;
8
9use proc_macro2::TokenStream as TokenStream2;
10
11fn module_path_literal() -> TokenStream2 {
12    quote! {
13        module_path!()
14    }
15}
16
17// Global kayıt için static map
18static EXPORTS: Lazy<Mutex<HashMap<String, String>>> = Lazy::new(|| Mutex::new(HashMap::new()));
19
20fn register_export(name: &str, path: &str) -> Result<(), String> {
21    let mut exports = EXPORTS.lock().unwrap();
22    if let Some(existing) = exports.get(name) {
23        if existing != path {
24            return Err(format!(
25                "Name collision detected: '{}' is already exported in '{}'",
26                name, existing
27            ));
28        }
29    }
30    exports.insert(name.to_string(), path.to_string());
31    Ok(())
32}
33
34#[proc_macro_attribute]
35pub fn export(_attr: TokenStream, item: TokenStream) -> TokenStream {
36    let item = parse_macro_input!(item as Item);
37    process_export(item, false)
38}
39
40#[proc_macro_attribute]
41pub fn export_fullpath(_attr: TokenStream, item: TokenStream) -> TokenStream {
42    let item = parse_macro_input!(item as Item);
43    process_export(item, true)
44}
45
46fn process_export(item: Item, use_fullpath: bool) -> TokenStream {
47    let (name, module_path) = match &item {
48        Item::Struct(s) => (&s.ident, module_path_literal()),
49        Item::Enum(e) => (&e.ident, module_path_literal()),
50        Item::Fn(f) => (&f.sig.ident, module_path_literal()),
51        _ => panic!("Export only supports structs, enums, and functions"),
52    };
53
54    let name_str = name.to_string();
55    let export_path = if use_fullpath {
56        quote! { concat!(#module_path, "::", stringify!(#name)) }
57    } else {
58        quote! { stringify!(#name) }
59    };
60
61    // Compile-time collision check
62    if let Err(err) = register_export(&name_str, &format!("{}::{}", module_path, name_str)) {
63        return syn::Error::new_spanned(&item, err)
64            .to_compile_error()
65            .into();
66    }
67
68    quote! {
69        #item
70
71        inventory::submit! {
72            crate::ExportItem {
73                name: #export_path,
74                module_path: #module_path,
75                full_path: concat!(#module_path, "::", stringify!(#name)),
76                is_fullpath: #use_fullpath
77            }
78        }
79    }.into()
80}