const_dispatch_proc_macros/
_mod.rs

1//! Crate not intended for direct use.
2//! Use https:://docs.rs/const-dispatch instead.
3// Templated by `cargo-generate` using https://github.com/danielhenrymantilla/proc-macro-template
4#![allow(nonstandard_style, unused_imports)]
5
6use ::core::{
7    mem,
8    ops::Not as _,
9};
10use ::proc_macro::*;
11
12macro_rules! tts {
13    [
14        $($tt:expr),* $(,)?
15    ] => (
16        [
17            $(
18                TokenTree::from($tt),
19            )*
20        ]
21        .into_iter()
22        .collect::<TokenStream>()
23    );
24}
25
26#[proc_macro_attribute]
27#[doc(hidden)] /** Not part of the public API */ pub
28fn ඞexterminate(
29    args: TokenStream,
30    _input: TokenStream,
31) -> TokenStream
32{
33    assert!(args.is_empty());
34    TokenStream::new()
35}
36
37#[proc_macro_derive(ConstDispatch)] pub
38fn __(
39    input: TokenStream
40) -> TokenStream
41{
42    let to_be_braced = Iterator::chain(
43        tts![
44            Punct::new('#', Spacing::Alone),
45            Group::new(
46                Delimiter::Bracket,
47                tts![
48                    Punct::new(':', Spacing::Joint),
49                    Punct::new(':', Spacing::Alone),
50                    Ident::new("const_dispatch", Span::mixed_site()),
51                    Punct::new(':', Spacing::Joint),
52                    Punct::new(':', Spacing::Alone),
53                    Ident::new("ඞ", Span::mixed_site()),
54                    Punct::new(':', Spacing::Joint),
55                    Punct::new(':', Spacing::Alone),
56                    Ident::new("exterminate", Span::mixed_site()),
57                ]
58            )
59        ]
60            .into_iter()
61        ,
62        input,
63    );
64    tts![
65        Punct::new(':', Spacing::Joint),
66        Punct::new(':', Spacing::Alone),
67        Ident::new("const_dispatch", Span::mixed_site()),
68        Punct::new(':', Spacing::Joint),
69        Punct::new(':', Spacing::Alone),
70        Ident::new("ඞ", Span::mixed_site()),
71        Punct::new(':', Spacing::Joint),
72        Punct::new(':', Spacing::Alone),
73        Ident::new("derive_ConstDispatch", Span::mixed_site()),
74        Punct::new('!', Spacing::Alone),
75        Group::new(Delimiter::Brace, to_be_braced.collect()),
76    ]
77}
78
79#[proc_macro]
80#[doc(hidden)] /** Not part of the public API */ pub
81fn ඞimpl_for_u8(
82    _input: TokenStream,
83) -> TokenStream
84{
85    // One-off helper macro.
86    // Hygiene plays no role in the output, so we simplify the `syn`-less impl by using strings.
87    let branches_const =
88        (0..=u8::MAX)
89            .map(|n| format!(r#"
90                {n}_u8 => {{
91                    const $C: ::core::primitive::u8 = {n};
92                    {{
93                        $crate::ඞ::try_hide!($scrutinee);
94                        $body
95                    }}
96                }},
97            "#))
98            .collect::<String>()
99    ;
100    let branches_macro =
101        (0..=u8::MAX)
102            .map(|n| format!(r#"
103                {n}_u8 => {{
104                    $crate::ඞ::try_hide!($scrutinee);
105                    __emit__! {{ {n} }}
106                }},
107            "#))
108            .collect::<String>()
109    ;
110    format!(r#"
111        #[doc(hidden)]
112        #[macro_export]
113        macro_rules! u8 {{
114            (
115                $scrutinee:expr,
116                |const $C:ident| $body:block
117            ) => (
118                match $scrutinee {{
119                    {branches_const}
120                }}
121            );
122
123            (
124                $scrutinee:expr,
125                $macro_input:tt => $macro_output:tt
126            ) => ({{
127                macro_rules! __emit__ {{ $macro_input => $macro_output }}
128                match $scrutinee {{
129                    {branches_macro}
130                }}
131            }});
132        }}
133    "#).parse().unwrap()
134}
135
136/// If `input = <identifier ≠ self>`, it emits a `let <identifier>: !;` to prevent the
137/// `const`-dispatched code from accidently using the runtime value rather than the `const`.
138#[proc_macro]
139#[doc(hidden)] /** Not part of the public API */ pub
140fn ඞtry_hide(
141    input: TokenStream,
142) -> TokenStream
143{
144    let ident = match input.into_iter().next().unwrap() {
145        TokenTree::Group(g) if g.delimiter() == Delimiter::None => {
146            let mut inner_tts = g.stream().into_iter();
147            match (inner_tts.next(), inner_tts.next()) {
148                (Some(TokenTree::Ident(ident)), None) if ident.to_string() != "self" => ident,
149                _ => return <_>::default(),
150            }
151        },
152        // currently not a reachable code path off a `$scrutinee:expr` arg, but who knows.
153        TokenTree::Ident(ident) if ident.to_string() != "self" => ident,
154        _ => return <_>::default(),
155    };
156    let allow_unused = Group::new(
157        Delimiter::Bracket,
158        tts![
159            Ident::new("allow", Span::mixed_site()),
160            Group::new(
161                Delimiter::Parenthesis,
162                tts![
163                    Ident::new("unused", Span::mixed_site()),
164                ],
165            ),
166        ],
167    );
168    tts![
169        Punct::new('#', Spacing::Alone), allow_unused.clone(),
170        // fn #ident() {}
171        Ident::new("fn", Span::mixed_site()),
172        ident.clone(),
173        Group::new(Delimiter::Parenthesis, <_>::default()),
174        Group::new(Delimiter::Brace, <_>::default()),
175
176        Punct::new('#', Spacing::Alone), allow_unused,
177        // let #ident: ::const_dispatch::ඞ::Never;
178        Ident::new("let", Span::mixed_site()),
179        ident,
180        Punct::new(':', Spacing::Alone),
181
182        Punct::new(':', Spacing::Joint),
183        Punct::new(':', Spacing::Alone),
184        Ident::new("const_dispatch", Span::mixed_site()),
185        Punct::new(':', Spacing::Joint),
186        Punct::new(':', Spacing::Alone),
187        Ident::new("ඞ", Span::mixed_site()),
188        Punct::new(':', Spacing::Joint),
189        Punct::new(':', Spacing::Alone),
190        Ident::new("Never", Span::mixed_site()),
191
192        Punct::new(';', Spacing::Alone),
193    ]
194}