1extern crate proc_macro;
2
3use std::collections::hash_map;
4use std::hash::Hasher;
5
6use proc_macro2::{Ident, Span, TokenStream};
7use quote::quote;
8use syn::parse::{Parse, ParseStream, Result};
9use syn::{bracketed, parse_macro_input, Path, Token};
10
11struct Input {
12 krate: Option<Path>,
13 expr: TokenStream,
14}
15
16impl Parse for Input {
17 fn parse(input: ParseStream) -> Result<Self> {
18 Ok(Input {
19 krate: {
20 if input.peek(Token![#]) && input.peek2(Token![!]) {
22 input.parse::<Token![#]>()?;
23 input.parse::<Token![!]>()?;
24 let content;
25 bracketed!(content in input);
26 content.parse::<Token![crate]>()?;
27 content.parse::<Token![=]>()?;
28 let krate = content.parse()?;
29 Some(krate)
30 } else {
31 None
32 }
33 },
34 expr: input.parse()?,
35 })
36 }
37}
38
39#[proc_macro]
40#[doc(hidden)]
41pub fn submit(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
42 let input = parse_macro_input!(input as Input);
43
44 let expr = input.expr;
45 let init = Ident::new(&format!("__init{}", hash(&expr)), Span::call_site());
46 let prefix = match input.krate {
47 Some(krate) => quote!(#krate::),
48 None => quote!(),
49 };
50
51 let expanded = quote! {
52 #[allow(non_upper_case_globals)]
53 #[#prefix inventory::ctor]
54 fn #init() {
55 #prefix inventory::submit({ #expr });
76 }
77 };
78
79 proc_macro::TokenStream::from(expanded)
80}
81
82fn hash(input: &TokenStream) -> u64 {
83 let mut hasher = hash_map::DefaultHasher::new();
84 hasher.write(input.to_string().as_bytes());
85 hasher.finish()
86}