byte_strings_proc_macro/
as_bytes.rs

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