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