ctor_proc_macro/
lib.rs

1use std::iter::FromIterator;
2
3use proc_macro::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree};
4
5#[proc_macro_attribute]
6pub fn ctor(attribute: TokenStream, item: TokenStream) -> TokenStream {
7    generate("ctor", "ctor", attribute, item)
8}
9
10// Legacy dtor macro.
11
12#[proc_macro_attribute]
13pub fn dtor(attribute: TokenStream, item: TokenStream) -> TokenStream {
14    generate("dtor", "ctor", attribute, item)
15}
16
17/// Generates the equivalent of this Rust code as a TokenStream:
18///
19/// ```nocompile
20/// ::ctor::__support::ctor_parse!(#[ctor] fn foo() { ... });
21/// ::dtor::__support::dtor_parse!(#[dtor] fn foo() { ... });
22/// ```
23fn generate(
24    macro_type: &str,
25    macro_crate: &str,
26    attribute: TokenStream,
27    item: TokenStream,
28) -> TokenStream {
29    let mut inner = TokenStream::new();
30
31    // Search for crate_path in attributes
32    let mut crate_path = None;
33    let mut tokens = attribute.clone().into_iter().peekable();
34    while let Some(token) = tokens.next() {
35        if let TokenTree::Ident(ident) = &token {
36            if ident.to_string() == "crate_path" {
37                // Look for =
38                if let Some(TokenTree::Punct(punct)) = tokens.next() {
39                    if punct.as_char() == '=' {
40                        // Collect tokens until comma or end
41                        let mut path = TokenStream::new();
42                        while let Some(token) = tokens.peek() {
43                            match token {
44                                TokenTree::Punct(p) if p.as_char() == ',' => {
45                                    tokens.next();
46                                    break;
47                                }
48                                _ => {
49                                    path.extend(std::iter::once(tokens.next().unwrap()));
50                                }
51                            }
52                        }
53                        crate_path = Some(path);
54                        break;
55                    }
56                }
57            }
58        }
59    }
60
61    #[cfg(feature = "used_linker")]
62    inner.extend([
63        TokenTree::Punct(Punct::new('#', Spacing::Alone)),
64        TokenTree::Group(Group::new(
65            Delimiter::Bracket,
66            TokenStream::from_iter([
67                TokenTree::Ident(Ident::new("feature", Span::call_site())),
68                TokenTree::Group(Group::new(
69                    Delimiter::Parenthesis,
70                    TokenStream::from_iter([TokenTree::Ident(Ident::new(
71                        "used_linker",
72                        Span::call_site(),
73                    ))]),
74                )),
75            ]),
76        )),
77    ]);
78
79    #[cfg(feature = "__warn_on_missing_unsafe")]
80    inner.extend([
81        TokenTree::Punct(Punct::new('#', Spacing::Alone)),
82        TokenTree::Group(Group::new(
83            Delimiter::Bracket,
84            TokenStream::from_iter([
85                TokenTree::Ident(Ident::new("feature", Span::call_site())),
86                TokenTree::Group(Group::new(
87                    Delimiter::Parenthesis,
88                    TokenStream::from_iter([TokenTree::Ident(Ident::new(
89                        "__warn_on_missing_unsafe",
90                        Span::call_site(),
91                    ))]),
92                )),
93            ]),
94        )),
95    ]);
96
97    if attribute.is_empty() {
98        // #[ctor]
99        inner.extend([
100            TokenTree::Punct(Punct::new('#', Spacing::Alone)),
101            TokenTree::Group(Group::new(
102                Delimiter::Bracket,
103                TokenStream::from_iter([TokenTree::Ident(Ident::new(
104                    macro_type,
105                    Span::call_site(),
106                ))]),
107            )),
108        ]);
109    } else {
110        inner.extend([
111            TokenTree::Punct(Punct::new('#', Spacing::Alone)),
112            TokenTree::Group(Group::new(
113                Delimiter::Bracket,
114                TokenStream::from_iter([
115                    TokenTree::Ident(Ident::new(macro_type, Span::call_site())),
116                    TokenTree::Group(Group::new(Delimiter::Parenthesis, attribute)),
117                ]),
118            )),
119        ]);
120    }
121
122    inner.extend(item);
123
124    let mut invoke = crate_path.unwrap_or_else(|| {
125        TokenStream::from_iter([
126            TokenTree::Punct(Punct::new(':', Spacing::Joint)),
127            TokenTree::Punct(Punct::new(':', Spacing::Alone)),
128            TokenTree::Ident(Ident::new(macro_crate, Span::call_site())),
129        ])
130    });
131
132    invoke.extend([
133        TokenTree::Punct(Punct::new(':', Spacing::Joint)),
134        TokenTree::Punct(Punct::new(':', Spacing::Alone)),
135        TokenTree::Ident(Ident::new("__support", Span::call_site())),
136        TokenTree::Punct(Punct::new(':', Spacing::Joint)),
137        TokenTree::Punct(Punct::new(':', Spacing::Alone)),
138        TokenTree::Ident(Ident::new(
139            &format!("{}_parse", macro_type),
140            Span::call_site(),
141        )),
142        TokenTree::Punct(Punct::new('!', Spacing::Alone)),
143        TokenTree::Group(Group::new(Delimiter::Parenthesis, inner)),
144        TokenTree::Punct(Punct::new(';', Spacing::Alone)),
145    ]);
146
147    invoke
148}