extern crate proc_macro;
use quote::quote;
use syn::{
parse::{Parse, ParseStream, Result},
parse_macro_input,
};
struct RealCString {
string: String,
}
impl Parse for RealCString {
fn parse(input: ParseStream) -> Result<Self> {
if let syn::Lit::Str(str) = input.parse()? {
Ok(RealCString {
string: str.value(),
})
} else {
Err(input.error("expected Str instead of ByteStr"))
}
}
}
enum TransformType {
CString,
CWString,
}
impl TransformType {
fn max_char(&self) -> u32 {
match self {
TransformType::CString => 0xff,
TransformType::CWString => 0xffff,
}
}
}
use TransformType::{CString, CWString};
fn transform(
input: RealCString,
transform_type: TransformType,
) -> proc_macro::TokenStream {
let stream = match transform_type {
CString | CWString => {
let bytes: Vec<proc_macro2::TokenStream> = input
.string
.chars()
.to_owned()
.enumerate()
.map(|(offset, cur_char)| {
let out: char = if cur_char as u32 <= transform_type.max_char() {
cur_char
} else {
return quote! {
compile_error!(concat!("Unsupported character \"", #cur_char, "\" at offset ", #offset)),
};
};
match transform_type {
CString => {
let res: i8 = out as i8;
quote! {#res,}
}
CWString => {
let res: i16 = out as i16;
quote! {#res,}
}
}
})
.collect();
match transform_type {
CString => quote! {
&[#(#bytes)* 0i8,] as *const i8
},
CWString => quote! {
&[#(#bytes)* 0i16,] as *const i16
},
}
}
};
proc_macro::TokenStream::from(stream)
}
#[proc_macro]
pub fn real_c_string(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
transform(
parse_macro_input!(input as RealCString),
TransformType::CString,
)
}
#[proc_macro]
pub fn real_c_wstring(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
transform(
parse_macro_input!(input as RealCString),
TransformType::CWString,
)
}