mod parse;
struct UnpackArgs {
fmt: String,
data: syn::Expr,
}
impl syn::parse::Parse for UnpackArgs {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let fmt = input.parse::<syn::LitStr>()?.value();
_ = input.parse::<syn::Token![,]>()?;
let data = input.parse()?;
Ok(Self { fmt, data })
}
}
struct PackArgs {
fmt: String,
args: Vec<syn::Expr>,
}
impl syn::parse::Parse for PackArgs {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let fmt = input.parse::<syn::LitStr>()?.value();
_ = input.parse::<syn::Token![,]>()?;
let args = input
.parse_terminated(syn::Expr::parse, syn::Token![,])?
.into_iter()
.collect();
Ok(Self { fmt, args })
}
}
#[proc_macro]
pub fn pack(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let PackArgs { fmt, args } = syn::parse_macro_input!(input as PackArgs);
let (little_endian, types) =
parse::fmt_parser::fmt_pack(&fmt).unwrap_or_else(|e| panic!("Invalid format string: {e}"));
if types.len() != args.len() {
panic!(
"Number of format specifiers ({}) does not match number of arguments ({})",
types.len(),
args.len()
);
}
let arg_names = (0..args.len())
.map(|i| syn::Ident::new(&format!("arg{i}"), proc_macro2::Span::call_site()))
.collect::<Vec<_>>();
let fn_args = arg_names
.iter()
.zip(types.iter())
.map(|(arg, ty)| quote::quote! { #arg: #ty })
.collect::<Vec<_>>();
quote::quote! {{
fn pack( #( #fn_args ),* ) -> Vec<u8>
{
let mut buf = Vec::new();
#(
if #little_endian {
<#types as ::bunpack::Pack>::pack_le(#arg_names, &mut buf);
} else {
<#types as ::bunpack::Pack>::pack_be(#arg_names, &mut buf);
}
)*
buf
}
pack( #( #args ),* )
}}
.into()
}
#[proc_macro]
pub fn unpack(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let UnpackArgs { fmt, data } = syn::parse_macro_input!(input as UnpackArgs);
let (little_endian, types) = parse::fmt_parser::fmt_unpack(&fmt)
.unwrap_or_else(|e| panic!("Invalid format string: {e}"));
quote::quote! {{
fn unpack<T: AsRef<[u8]>>(data: T) -> ( #( #types ),* )
{
let mut data: &[u8] = data.as_ref();
(#(
if #little_endian {
<#types as ::bunpack::Unpack>::unpack_le(&mut data)
} else {
<#types as ::bunpack::Unpack>::unpack_be(&mut data)
}
),*)
}
unpack(#data)
}}
.into()
}