cstr_macros/
lib.rs

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(&quote!($($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}