byte_array_literals/
lib.rs

1extern crate proc_macro;
2
3use proc_macro::{Delimiter, Group, Literal, Punct, Spacing, TokenStream, TokenTree};
4
5/// Expand a `str` literal into a byte array.
6#[proc_macro]
7pub fn str(input: TokenStream) -> TokenStream {
8    let rv = convert_str(input);
9
10    vec![TokenTree::Group(Group::new(
11        Delimiter::Bracket,
12        rv.into_iter().collect(),
13    ))]
14    .into_iter()
15    .collect()
16}
17
18/// The same as `str` but appends a `'\n'`.
19#[proc_macro]
20pub fn str_nl(input: TokenStream) -> TokenStream {
21    let mut rv = convert_str(input);
22
23    rv.push(TokenTree::Literal(Literal::u8_suffixed(b'\n')));
24
25    vec![TokenTree::Group(Group::new(
26        Delimiter::Bracket,
27        rv.into_iter().collect(),
28    ))]
29    .into_iter()
30    .collect()
31}
32
33fn convert_str(input: TokenStream) -> Vec<TokenTree> {
34    let mut it = input.into_iter();
35
36    let mut tokens = Vec::new();
37    match it.next() {
38        Some(TokenTree::Literal(l)) => {
39            for b in to_string(l).into_bytes() {
40                tokens.push(TokenTree::Literal(Literal::u8_suffixed(b)));
41                tokens.push(TokenTree::Punct(Punct::new(',', Spacing::Alone)));
42            }
43        }
44        _ => panic!(),
45    }
46
47    assert!(it.next().is_none());
48    tokens
49}
50
51fn to_string(lit: Literal) -> String {
52    let formatted = lit.to_string();
53
54    let mut it = formatted.chars();
55    assert_eq!(it.next(), Some('"'));
56
57    let mut rv = String::new();
58    loop {
59        match it.next() {
60            Some('"') => match it.next() {
61                Some(_) => panic!(),
62                None => break,
63            },
64            Some('\\') => match it.next() {
65                Some('x') => {
66                    let hi = it.next().unwrap().to_digit(16).unwrap();
67                    let lo = it.next().unwrap().to_digit(16).unwrap();
68                    let v = (hi << 16) | lo;
69                    rv.push(v as u8 as char);
70                }
71                Some('u') => {
72                    assert_eq!(it.next(), Some('{'));
73                    let mut c = it.next().unwrap();
74                    let mut ch = 0;
75                    while let Some(v) = c.to_digit(16) {
76                        ch *= 16;
77                        ch |= v;
78                        c = it.next().unwrap();
79                    }
80                    assert_eq!(c, '}');
81                    rv.push(::std::char::from_u32(ch).unwrap());
82                }
83                Some('0') => rv.push('\0'),
84                Some('\\') => rv.push('\\'),
85                Some('\"') => rv.push('\"'),
86                Some('r') => rv.push('\r'),
87                Some('n') => rv.push('\n'),
88                Some('t') => rv.push('\t'),
89                Some(_) => panic!(),
90                None => panic!(),
91            },
92            Some(c) => rv.push(c),
93            None => panic!(),
94        }
95    }
96
97    rv
98}