inventory_impl/
lib.rs

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                // #![crate = gflags]
21                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            // TODO: once existential type is stable, store the caller's
56            // expression into a static and string those statics together into
57            // an intrusive linked list without needing allocation.
58            //
59            //     existential type This;
60            //
61            //     static mut VALUE: Option<inventory::Node<This>> = None;
62            //
63            //     fn value() -> This {
64            //         #expr
65            //     }
66            //
67            //     unsafe {
68            //         VALUE = Some(inventory::Node {
69            //             value: value(),
70            //             next: None,
71            //         });
72            //         inventory::submit(VALUE.as_mut().unwrap());
73            //     }
74
75            #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}