1#[macro_use]
2extern crate procedural_masquerade;
3extern crate proc_macro;
4#[cfg(test)]
5#[macro_use]
6extern crate quote;
7extern crate syn;
8
9use std::ascii::escape_default;
10use std::ffi::CString;
11
12define_proc_macros! {
13 #[allow(non_snake_case)]
14 pub fn cstr_internal__build_bytes(input: &str) -> String {
15 let bytes = build_bytes(input);
16 format!("const BYTES: &'static [u8] = {};", bytes)
17 }
18}
19
20fn input_to_string(input: &str) -> String {
21 if let Ok(s) = syn::parse_str::<syn::LitStr>(input) {
22 return s.value();
23 }
24 if let Ok(i) = syn::parse_str::<syn::Ident>(input) {
25 return i.to_string();
26 }
27 panic!("expected a string literal or an identifier, got {}", input)
28}
29
30fn build_bytes(input: &str) -> String {
31 let s = input_to_string(input);
32 let cstr = match CString::new(s.as_bytes()) {
33 Ok(s) => s,
34 _ => panic!("literal must not contain NUL byte")
35 };
36 let mut bytes = Vec::new();
37 bytes.extend(br#"b""#);
38 bytes.extend(cstr.as_bytes().iter().flat_map(|&b| escape_default(b)));
39 bytes.extend(br#"\0""#);
40 String::from_utf8(bytes).unwrap()
41}
42
43#[cfg(test)]
44mod tests {
45 use super::build_bytes;
46
47 macro_rules! build_bytes {
48 ($($t:tt)*) => {
49 build_bytes("e!($($t)*).to_string())
50 }
51 }
52 macro_rules! result {
53 ($($t:tt)*) => {
54 quote!($($t)*).to_string()
55 }
56 }
57
58 #[test]
59 fn test_build_bytes() {
60 assert_eq!(build_bytes!("aaa"), result!(b"aaa\0"));
61 assert_eq!(build_bytes!("\t\n\r\"\\'"), result!(b"\t\n\r\"\\\'\0"));
62 assert_eq!(build_bytes!("\x01\x02 \x7f"), result!(b"\x01\x02 \x7f\0"));
63 assert_eq!(build_bytes!("你好"), result!(b"\xe4\xbd\xa0\xe5\xa5\xbd\0"));
64 assert_eq!(build_bytes!(foobar), result!(b"foobar\0"));
65 }
66
67 #[test]
68 #[should_panic]
69 fn test_build_bytes_nul_inside() {
70 build_bytes!("a\x00a");
71 }
72}