ctor_proc_macro/
lib.rs

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