byte_strings_proc_macro/
concat_bytes.rs

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