byte_strings_proc_macro/
identity_non_null.rs

1::cfg_if::cfg_if![ if #[cfg(feature = "proc-macro-hygiene")]
2{
3    #[cfg(any())]
4    mod objective {
5        macro_rules! identity_non_null {(
6                $input_bytestr_literal:expr
7        ) => (
8            if $input_bytestr_literal.contains(&0) {
9                compile_error!("input contains a null byte")
10            } else {
11                $input_bytestr_literal
12            }
13        )}
14    }
15
16    #[doc(hidden)]
17    #[proc_macro]
18    pub fn identity_non_null (
19        input: proc_macro::TokenStream,
20    ) -> proc_macro::TokenStream
21    {
22        let input_expr = syn::parse_macro_input!(input as syn::Expr);
23
24        match input_expr {
25            // if the input expression is a byte string literal,
26            // return it as is iff it contains no null byte, else fail
27            syn::Expr::Lit(
28                syn::ExprLit {
29                    lit: syn::Lit::ByteStr(ref bytes),
30                    ..
31                }
32            ) => {
33                if bytes.value().contains(&0) {
34                    throw!(input_expr.span()=>
35                        "input contains a null byte"
36                    );
37                } else {
38                    proc_macro::TokenStream::from(quote::quote! {
39                        input_expr
40                    })
41                }
42            },
43
44            // else, the macro was misused
45            _ => throw!(input_expr.span()=>
46                "expected a byte string literal"
47            ),
48        }
49    }
50}
51else
52{
53    #[cfg(any())]
54    mod objective {
55        macro_rules! const_identity_non_null {(
56            const $const_literal_name:ident = identity_non_null!(
57                $input_bytestr_literal:expr
58            );
59        ) => (
60            const $const_literal_name: &[u8; _] = b"_";
61        )}
62    }
63
64    struct ConstIdentityNonNull {
65        const_literal_name: syn::Ident,
66        input_bytestr_literal: syn::Expr,
67    }
68
69    impl Parse for ConstIdentityNonNull
70    {
71        fn parse (input: syn::parse::ParseStream) -> syn::parse::Result<Self>
72        {
73            macro_rules! parse_token {[$tt:tt] => (
74                input.parse::<syn::Token![$tt]>()?
75            )}
76
77            parse_token![ const ];
78            let const_literal_name: syn::Ident = input.parse()?;
79            parse_token![ =     ];
80            input.parse::<kw::identity_non_null>()?;
81            parse_token![ !     ];
82
83            let input_bytestr_literal: syn::Expr = syn::group::
84                parse_parens(&input)?
85                    .content
86                    .parse()?
87            ;
88            parse_token![ ;     ];
89            Ok(ConstIdentityNonNull {
90                const_literal_name,
91                input_bytestr_literal,
92            })
93        }
94    }
95
96    #[proc_macro]
97    pub fn const_identity_non_null (
98        input: proc_macro::TokenStream,
99    ) -> proc_macro::TokenStream
100    {
101        let ConstIdentityNonNull {
102            const_literal_name,
103            input_bytestr_literal: input_expr,
104        } = syn::parse_macro_input!(input);
105
106        match input_expr {
107            // if the input expression is a byte string literal,
108            // return it as is iff it contains no null byte, else fail
109            syn::Expr::Lit(
110                syn::ExprLit {
111                    lit: syn::Lit::ByteStr(ref bytes),
112                    ..
113                }
114            ) => {
115                if bytes.value().contains(&0) {
116                    throw!(input_expr.span()=>
117                        "input contains a null byte"
118                    );
119                } else {
120                    let len = syn::Expr::
121                        Lit(syn::ExprLit {
122                            attrs: vec![],
123
124                            lit: syn::Lit::Int(syn::LitInt::new(
125                                // value
126                                bytes.value().len() as u64,
127
128                                // suffix
129                                syn::IntSuffix::Usize,
130
131                                // Span
132                                input_expr.span().clone(),
133                            ))
134                        })
135                    ;
136
137                    proc_macro::TokenStream::from(quote::quote! {
138                        const #const_literal_name
139                            : &'static [u8; #len]
140                            = #input_expr
141                        ;
142                    })
143                }
144            },
145
146            // else, the macro was misused
147            _ => throw!(input_expr.span()=>
148                "expected a byte string literal"
149            ),
150        }
151    }
152}];