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