byte_strings_proc_macro/
concat_bytes.rs1::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 bytes.len() as u64,
143
144 syn::IntSuffix::Usize,
146
147 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 &bytes,
160
161 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}];