use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use syn::{ parse2, Error, Lit, LitByteStr };
use quote::quote_spanned;
#[proc_macro]
pub fn zstr(input: TokenStream) -> TokenStream {
expand_zstr(input.into())
.unwrap_or_else(Error::into_compile_error)
.into()
}
fn expand_zstr(input: TokenStream2) -> Result<TokenStream2, Error> {
let literal: Lit = parse2(input)?;
let span = literal.span();
let mut bytes = match literal {
Lit::Str(lit) => lit.value().into_bytes(),
Lit::ByteStr(lit) => lit.value(),
_ => return Err(Error::new(span, "expected a string or byte string literal")),
};
if let Some(index) = bytes.iter().position(|&b| b == 0x00) {
let message = format!("C string contains an embedded NUL byte at index {}", index);
return Err(Error::new(span, message));
}
bytes.reserve_exact(1);
bytes.push(0x00);
let bstr = LitByteStr::new(&bytes, span);
Ok(quote_spanned!{
span => unsafe {
::std::ffi::CStr::from_bytes_with_nul_unchecked(#bstr)
}
})
}