#![crate_type = "proc-macro"]
extern crate proc_macro;
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
use syn::{
parse_macro_input, Data, DataEnum, DataStruct, DeriveInput, Field, Fields, FieldsNamed,
FieldsUnnamed, GenericArgument, Generics, Ident, Meta, PathArguments, Type,
};
fn has_attr(field: &Field, name: &str) -> bool {
field.attrs.iter().any(|attr| {
let Meta::List(list) = &attr.meta else {
return false;
};
list.path.is_ident("msgpacker")
&& list
.tokens
.clone()
.into_iter()
.any(|t| t.to_string() == name)
})
}
fn is_slice_of_u8(ty: &Type) -> bool {
let Type::Reference(r) = ty else { return false };
let Type::Slice(s) = r.elem.as_ref() else {
return false;
};
let Type::Path(p) = s.elem.as_ref() else {
return false;
};
p.path.segments.last().is_some_and(|s| s.ident == "u8")
}
fn is_vec_of_u8(ty: &Type) -> bool {
let Type::Path(p) = ty else { return false };
let Some(last) = p.path.segments.last() else {
return false;
};
if last.ident != "Vec" {
return false;
}
let PathArguments::AngleBracketed(args) = &last.arguments else {
return false;
};
if args.args.len() != 1 {
return false;
}
let Some(GenericArgument::Type(Type::Path(inner))) = args.args.first() else {
return false;
};
inner.path.segments.last().is_some_and(|s| s.ident == "u8")
}
fn is_option_type(ty: &Type) -> bool {
let Type::Path(p) = ty else { return false };
let Some(last) = p.path.segments.last() else {
return false;
};
last.ident == "Option"
&& matches!(&last.arguments, PathArguments::AngleBracketed(args) if args.args.len() == 1)
}
fn is_vec_of_non_u8(ty: &Type) -> bool {
if is_vec_of_u8(ty) {
return false;
}
let Type::Path(p) = ty else { return false };
let Some(last) = p.path.segments.last() else {
return false;
};
if last.ident != "Vec" {
return false;
}
let PathArguments::AngleBracketed(args) = &last.arguments else {
return false;
};
args.args.len() == 1
}
fn binary_named_stmts(ident: &Ident, ty: &Type) -> (TokenStream2, TokenStream2, TokenStream2) {
if is_option_type(ty) {
(
quote! {
n += ::msgpacker::pack_bytes_option(
buf,
self.#ident.as_ref().map(::core::convert::AsRef::as_ref),
);
},
quote! {
let #ident = ::msgpacker::unpack_bytes_option(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
t.map(::core::convert::From::from)
})?;
},
quote! {
let #ident = ::msgpacker::unpack_bytes_option_iter(bytes.by_ref()).map(|(nv, t)| {
n += nv;
t.map(::core::convert::From::from)
})?;
},
)
} else {
(
quote! {
n += ::msgpacker::pack_bytes(buf, ::core::convert::AsRef::<[u8]>::as_ref(&self.#ident));
},
quote! {
let #ident = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
::core::convert::From::from(<[u8]>::to_vec(t))
})?;
},
quote! {
let #ident = ::msgpacker::unpack_bytes_iter(bytes.by_ref()).map(|(nv, t)| {
n += nv;
::core::convert::From::from(t)
})?;
},
)
}
}
fn binary_named_unpack_borrowed(ident: &Ident, ty: &Type) -> TokenStream2 {
if is_option_type(ty) {
quote! {
let #ident = ::msgpacker::unpack_bytes_option_ref(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
t.map(::core::convert::From::from)
})?;
}
} else {
quote! {
let #ident = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
::core::convert::From::from(t)
})?;
}
}
}
fn emit_struct_impls(
name: &Ident,
generics: &Generics,
pack_stmts: &[TokenStream2],
unpack_stmts: &[TokenStream2],
unpack_iter_stmts: &[TokenStream2],
construct: TokenStream2,
) -> TokenStream2 {
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
quote! {
impl #impl_generics ::msgpacker::Packable for #name #ty_generics #where_clause {
fn pack<T>(&self, buf: &mut T) -> usize
where
T: ::msgpacker::BufMut,
{
use ::msgpacker::Packable as _;
let mut n = 0usize;
#(#pack_stmts)*
n
}
}
impl #impl_generics ::msgpacker::Unpackable for #name #ty_generics #where_clause {
type Error = ::msgpacker::Error;
fn unpack_with_ofs(mut buf: &[u8]) -> Result<(usize, Self), Self::Error> {
let mut n = 0usize;
#(#unpack_stmts)*
Ok((n, #construct))
}
fn unpack_iter<I>(bytes: I) -> Result<(usize, Self), Self::Error>
where
I: IntoIterator<Item = u8>,
{
let mut bytes = bytes.into_iter();
let mut n = 0usize;
#(#unpack_iter_stmts)*
Ok((n, #construct))
}
}
}
}
fn named_field_stmts(ident: &Ident, field: &Field) -> (TokenStream2, TokenStream2, TokenStream2) {
let ty = &field.ty;
if has_attr(field, "binary") {
return binary_named_stmts(ident, ty);
}
if has_attr(field, "map") {
return (
quote! { n += ::msgpacker::pack_map(buf, &self.#ident); },
quote! {
let #ident = ::msgpacker::unpack_map(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
t
})?;
},
quote! {
let #ident = ::msgpacker::unpack_map_iter(bytes.by_ref()).map(|(nv, t)| {
n += nv;
t
})?;
},
);
}
if has_attr(field, "array") || is_vec_of_non_u8(ty) {
return (
quote! { n += ::msgpacker::pack_array(buf, &self.#ident); },
quote! {
let #ident = ::msgpacker::unpack_array(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
t
})?;
},
quote! {
let #ident = ::msgpacker::unpack_array_iter(bytes.by_ref()).map(|(nv, t)| {
n += nv;
t
})?;
},
);
}
if is_slice_of_u8(ty) {
return (
quote! { n += ::msgpacker::pack_bytes(buf, self.#ident); },
quote! {
let #ident = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
t
})?;
},
quote! {
let #ident = ::msgpacker::unpack_bytes_iter(bytes.by_ref()).map(|(nv, t)| {
n += nv;
t
})?;
},
);
}
if is_vec_of_u8(ty) {
return (
quote! { n += ::msgpacker::pack_bytes(buf, self.#ident.as_slice()); },
quote! {
let #ident = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
<[u8]>::to_vec(t)
})?;
},
quote! {
let #ident = ::msgpacker::unpack_bytes_iter(bytes.by_ref()).map(|(nv, t)| {
n += nv;
t
})?;
},
);
}
(
quote! { n += self.#ident.pack(buf); },
quote! {
let #ident = ::msgpacker::Unpackable::unpack_with_ofs(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
t
})?;
},
quote! {
let #ident = ::msgpacker::Unpackable::unpack_iter(bytes.by_ref()).map(|(nv, t)| {
n += nv;
t
})?;
},
)
}
fn impl_struct_named(name: &Ident, f: FieldsNamed, generics: &Generics) -> TokenStream2 {
let mut pack_stmts = Vec::new();
let mut unpack_stmts = Vec::new();
let mut unpack_iter_stmts = Vec::new();
let mut field_names = Vec::new();
for field in f.named {
let ident = field.ident.as_ref().cloned().unwrap();
let (pack, unpack, unpack_iter) = named_field_stmts(&ident, &field);
pack_stmts.push(pack);
unpack_stmts.push(unpack);
unpack_iter_stmts.push(unpack_iter);
field_names.push(ident);
}
emit_struct_impls(
name,
generics,
&pack_stmts,
&unpack_stmts,
&unpack_iter_stmts,
quote! { Self { #(#field_names),* } },
)
}
fn impl_struct_unnamed(name: &Ident, f: FieldsUnnamed, generics: &Generics) -> TokenStream2 {
let mut pack_stmts = Vec::new();
let mut unpack_stmts = Vec::new();
let mut unpack_iter_stmts = Vec::new();
let mut var_names = Vec::new();
for (i, field) in f.unnamed.into_iter().enumerate() {
if has_attr(&field, "map") {
todo!("unnamed map is not implemented for derive macro; implement the traits manually");
}
if has_attr(&field, "array") {
todo!(
"unnamed array is not implemented for derive macro; implement the traits manually"
);
}
let var = format_ident!("v{}", i);
let index = syn::Index::from(i);
if has_attr(&field, "binary") {
if is_option_type(&field.ty) {
pack_stmts.push(quote! {
n += ::msgpacker::pack_bytes_option(
buf,
self.#index.as_ref().map(::core::convert::AsRef::as_ref),
);
});
unpack_stmts.push(quote! {
let #var = ::msgpacker::unpack_bytes_option(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
t.map(::core::convert::From::from)
})?;
});
unpack_iter_stmts.push(quote! {
let #var = ::msgpacker::unpack_bytes_option_iter(bytes.by_ref()).map(|(nv, t)| {
n += nv;
t.map(::core::convert::From::from)
})?;
});
} else {
pack_stmts.push(quote! {
n += ::msgpacker::pack_bytes(buf, ::core::convert::AsRef::<[u8]>::as_ref(&self.#index));
});
unpack_stmts.push(quote! {
let #var = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
::core::convert::From::from(<[u8]>::to_vec(t))
})?;
});
unpack_iter_stmts.push(quote! {
let #var = ::msgpacker::unpack_bytes_iter(bytes.by_ref()).map(|(nv, t)| {
n += nv;
::core::convert::From::from(t)
})?;
});
}
var_names.push(var);
continue;
}
if is_vec_of_u8(&field.ty) {
pack_stmts.push(quote! { n += ::msgpacker::pack_bytes(buf, self.#index.as_slice()); });
unpack_stmts.push(quote! {
let #var = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
<[u8]>::to_vec(t)
})?;
});
unpack_iter_stmts.push(quote! {
let #var = ::msgpacker::unpack_bytes_iter(bytes.by_ref()).map(|(nv, t)| {
n += nv;
t
})?;
});
} else {
pack_stmts.push(quote! { n += self.#index.pack(buf); });
unpack_stmts.push(quote! {
let #var = ::msgpacker::Unpackable::unpack_with_ofs(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
t
})?;
});
unpack_iter_stmts.push(quote! {
let #var = ::msgpacker::Unpackable::unpack_iter(bytes.by_ref()).map(|(nv, t)| {
n += nv;
t
})?;
});
}
var_names.push(var);
}
emit_struct_impls(
name,
generics,
&pack_stmts,
&unpack_stmts,
&unpack_iter_stmts,
quote! { Self(#(#var_names),*) },
)
}
fn impl_struct_unit(name: &Ident, generics: &Generics) -> TokenStream2 {
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
quote! {
impl #impl_generics ::msgpacker::Packable for #name #ty_generics #where_clause {
fn pack<T>(&self, _buf: &mut T) -> usize
where
T: ::msgpacker::BufMut,
{
0
}
}
impl #impl_generics ::msgpacker::Unpackable for #name #ty_generics #where_clause {
type Error = ::msgpacker::Error;
fn unpack_with_ofs(_buf: &[u8]) -> Result<(usize, Self), Self::Error> {
Ok((0, Self))
}
fn unpack_iter<I>(_bytes: I) -> Result<(usize, Self), Self::Error>
where
I: IntoIterator<Item = u8>,
{
Ok((0, Self))
}
}
}
}
fn impl_enum(name: &Ident, data: DataEnum, generics: &Generics) -> TokenStream2 {
if data.variants.is_empty() {
todo!("empty enum is not implemented for derive macro; implement the traits manually");
}
let mut pack_arms = Vec::new();
let mut unpack_arms = Vec::new();
let mut unpack_iter_arms = Vec::new();
for (i, variant) in data.variants.into_iter().enumerate() {
let discriminant = variant
.discriminant
.map(|(_, d)| d)
.unwrap_or_else(|| syn::parse_str(&i.to_string()).unwrap());
let variant_ident = &variant.ident;
match variant.fields {
Fields::Named(f) => {
let mut field_names = Vec::new();
let mut pack_field_stmts = Vec::new();
let mut unpack_field_stmts = Vec::new();
let mut unpack_iter_field_stmts = Vec::new();
for field in &f.named {
let ident = field.ident.as_ref().unwrap();
field_names.push(ident.clone());
if has_attr(field, "binary") {
if is_option_type(&field.ty) {
pack_field_stmts.push(quote! {
n += ::msgpacker::pack_bytes_option(
buf,
#ident.as_ref().map(::core::convert::AsRef::as_ref),
);
});
unpack_field_stmts.push(quote! {
let #ident = ::msgpacker::unpack_bytes_option(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
t.map(::core::convert::From::from)
})?;
});
unpack_iter_field_stmts.push(quote! {
let #ident = ::msgpacker::unpack_bytes_option_iter(bytes.by_ref()).map(|(nv, t)| {
n += nv;
t.map(::core::convert::From::from)
})?;
});
} else {
pack_field_stmts.push(quote! {
n += ::msgpacker::pack_bytes(buf, ::core::convert::AsRef::<[u8]>::as_ref(#ident));
});
unpack_field_stmts.push(quote! {
let #ident = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
::core::convert::From::from(<[u8]>::to_vec(t))
})?;
});
unpack_iter_field_stmts.push(quote! {
let #ident = ::msgpacker::unpack_bytes_iter(bytes.by_ref()).map(|(nv, t)| {
n += nv;
::core::convert::From::from(t)
})?;
});
}
} else if is_vec_of_u8(&field.ty) {
pack_field_stmts
.push(quote! { n += ::msgpacker::pack_bytes(buf, #ident.as_slice()); });
unpack_field_stmts.push(quote! {
let #ident = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
<[u8]>::to_vec(t)
})?;
});
unpack_iter_field_stmts.push(quote! {
let #ident = ::msgpacker::unpack_bytes_iter(bytes.by_ref()).map(|(nv, t)| {
n += nv;
t
})?;
});
} else {
pack_field_stmts.push(quote! { n += #ident.pack(buf); });
unpack_field_stmts.push(quote! {
let #ident = ::msgpacker::Unpackable::unpack_with_ofs(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
t
})?;
});
unpack_iter_field_stmts.push(quote! {
let #ident = ::msgpacker::Unpackable::unpack_iter(bytes.by_ref()).map(|(nv, t)| {
n += nv;
t
})?;
});
}
}
pack_arms.push(quote! {
#name::#variant_ident { #(#field_names),* } => {
n += <u32 as ::msgpacker::Packable>::pack(&(#discriminant as u32), buf);
#(#pack_field_stmts)*
}
});
unpack_arms.push(quote! {
#discriminant => {
#(#unpack_field_stmts)*
slf = #name::#variant_ident { #(#field_names),* };
}
});
unpack_iter_arms.push(quote! {
#discriminant => {
#(#unpack_iter_field_stmts)*
slf = #name::#variant_ident { #(#field_names),* };
}
});
}
Fields::Unnamed(f) => {
let vars: Vec<Ident> = (0..f.unnamed.len())
.map(|j| format_ident!("t{}", j))
.collect();
let mut pack_field_stmts = Vec::new();
let mut unpack_field_stmts = Vec::new();
let mut unpack_iter_field_stmts = Vec::new();
for (var, field) in vars.iter().zip(f.unnamed.iter()) {
if has_attr(field, "binary") {
if is_option_type(&field.ty) {
pack_field_stmts.push(quote! {
n += ::msgpacker::pack_bytes_option(
buf,
#var.as_ref().map(::core::convert::AsRef::as_ref),
);
});
unpack_field_stmts.push(quote! {
let #var = ::msgpacker::unpack_bytes_option(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
t.map(::core::convert::From::from)
})?;
});
unpack_iter_field_stmts.push(quote! {
let #var = ::msgpacker::unpack_bytes_option_iter(bytes.by_ref()).map(|(nv, t)| {
n += nv;
t.map(::core::convert::From::from)
})?;
});
} else {
pack_field_stmts.push(quote! {
n += ::msgpacker::pack_bytes(buf, ::core::convert::AsRef::<[u8]>::as_ref(#var));
});
unpack_field_stmts.push(quote! {
let #var = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
::core::convert::From::from(<[u8]>::to_vec(t))
})?;
});
unpack_iter_field_stmts.push(quote! {
let #var = ::msgpacker::unpack_bytes_iter(bytes.by_ref()).map(|(nv, t)| {
n += nv;
::core::convert::From::from(t)
})?;
});
}
} else if is_vec_of_u8(&field.ty) {
pack_field_stmts
.push(quote! { n += ::msgpacker::pack_bytes(buf, #var.as_slice()); });
unpack_field_stmts.push(quote! {
let #var = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
<[u8]>::to_vec(t)
})?;
});
unpack_iter_field_stmts.push(quote! {
let #var = ::msgpacker::unpack_bytes_iter(bytes.by_ref()).map(|(nv, t)| {
n += nv;
t
})?;
});
} else {
pack_field_stmts.push(quote! { n += #var.pack(buf); });
unpack_field_stmts.push(quote! {
let #var = ::msgpacker::Unpackable::unpack_with_ofs(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
t
})?;
});
unpack_iter_field_stmts.push(quote! {
let #var = ::msgpacker::Unpackable::unpack_iter(bytes.by_ref()).map(|(nv, t)| {
n += nv;
t
})?;
});
}
}
pack_arms.push(quote! {
#name::#variant_ident(#(#vars),*) => {
n += <u32 as ::msgpacker::Packable>::pack(&(#discriminant as u32), buf);
#(#pack_field_stmts)*
}
});
unpack_arms.push(quote! {
#discriminant => {
#(#unpack_field_stmts)*
slf = #name::#variant_ident(#(#vars),*);
}
});
unpack_iter_arms.push(quote! {
#discriminant => {
#(#unpack_iter_field_stmts)*
slf = #name::#variant_ident(#(#vars),*);
}
});
}
Fields::Unit => {
pack_arms.push(quote! {
#name::#variant_ident => {
n += <u32 as ::msgpacker::Packable>::pack(&(#discriminant as u32), buf);
}
});
unpack_arms.push(quote! {
#discriminant => slf = #name::#variant_ident,
});
unpack_iter_arms.push(quote! {
#discriminant => slf = #name::#variant_ident,
});
}
}
}
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
quote! {
impl #impl_generics ::msgpacker::Packable for #name #ty_generics #where_clause {
fn pack<T>(&self, buf: &mut T) -> usize
where
T: ::msgpacker::BufMut,
{
use ::msgpacker::Packable as _;
let mut n = 0usize;
match self {
#(#pack_arms)*
}
n
}
}
impl #impl_generics ::msgpacker::Unpackable for #name #ty_generics #where_clause {
type Error = ::msgpacker::Error;
#[allow(unused_mut)]
fn unpack_with_ofs(mut buf: &[u8]) -> Result<(usize, Self), Self::Error> {
let (mut n, discriminant) =
<u32 as ::msgpacker::Unpackable>::unpack_with_ofs(buf)?;
buf = &buf[n..];
let slf;
match discriminant {
#(#unpack_arms)*
_ => return Err(::msgpacker::Error::InvalidEnumVariant),
}
Ok((n, slf))
}
fn unpack_iter<I>(bytes: I) -> Result<(usize, Self), Self::Error>
where
I: IntoIterator<Item = u8>,
{
let mut bytes = bytes.into_iter();
let (mut n, discriminant) =
<u32 as ::msgpacker::Unpackable>::unpack_iter(bytes.by_ref())?;
let slf;
match discriminant {
#(#unpack_iter_arms)*
_ => return Err(::msgpacker::Error::InvalidEnumVariant),
}
Ok((n, slf))
}
}
}
}
#[proc_macro_derive(MsgPacker, attributes(msgpacker))]
pub fn msg_packer(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;
let generics = &input.generics;
let tokens = match input.data {
Data::Struct(DataStruct {
fields: Fields::Named(f),
..
}) => impl_struct_named(name, f, generics),
Data::Struct(DataStruct {
fields: Fields::Unnamed(f),
..
}) => impl_struct_unnamed(name, f, generics),
Data::Struct(DataStruct {
fields: Fields::Unit,
..
}) => impl_struct_unit(name, generics),
Data::Enum(data) => impl_enum(name, data, generics),
Data::Union(_) => {
todo!(
"union support is not implemented for derive macro; implement the traits manually"
)
}
};
tokens.into()
}
fn resolve_borrow_lifetime(generics: &Generics) -> (syn::Lifetime, Option<syn::Generics>) {
match generics.lifetimes().next() {
Some(lt_def) => (lt_def.lifetime.clone(), None),
None => {
let lt: syn::Lifetime = syn::parse_quote!('__a);
let mut modified = generics.clone();
modified.params.insert(
0,
syn::GenericParam::Lifetime(syn::LifetimeParam::new(lt.clone())),
);
(lt, Some(modified))
}
}
}
fn emit_borrowed_struct_impl(
name: &Ident,
generics: &Generics,
borrow_lt: &syn::Lifetime,
impl_gen_src: &Generics,
pack_stmts: Option<&[TokenStream2]>,
unpack_stmts: &[TokenStream2],
construct: TokenStream2,
) -> TokenStream2 {
let (pack_impl_generics, ty_generics, pack_where_clause) = generics.split_for_impl();
let (impl_generics, _, where_clause) = impl_gen_src.split_for_impl();
let packable_impl = pack_stmts.map(|stmts| quote! {
impl #pack_impl_generics ::msgpacker::Packable for #name #ty_generics #pack_where_clause {
fn pack<T>(&self, buf: &mut T) -> usize
where
T: ::msgpacker::BufMut,
{
use ::msgpacker::Packable as _;
let mut n = 0usize;
#(#stmts)*
n
}
}
});
quote! {
#packable_impl
impl #impl_generics ::msgpacker::UnpackableBorrowed<#borrow_lt> for #name #ty_generics #where_clause {
type Error = ::msgpacker::Error;
fn unpack_with_ofs(mut buf: &#borrow_lt [u8]) -> Result<(usize, Self), Self::Error> {
let mut n = 0usize;
#(#unpack_stmts)*
Ok((n, #construct))
}
}
}
}
fn named_field_borrowed_stmt(
ident: &Ident,
field: &Field,
borrow_lt: &syn::Lifetime,
) -> TokenStream2 {
let ty = &field.ty;
if has_attr(field, "binary") {
return binary_named_unpack_borrowed(ident, ty);
}
if has_attr(field, "map") {
return quote! {
let #ident = ::msgpacker::unpack_map(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
t
})?;
};
}
if is_slice_of_u8(ty) || is_vec_of_u8(ty) {
return quote! {
let #ident = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
t.into()
})?;
};
}
if has_attr(field, "array") || is_vec_of_non_u8(ty) {
return quote! {
let #ident = ::msgpacker::unpack_array_borrowed::<_, _>(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
t
})?;
};
}
quote! {
let #ident = <_ as ::msgpacker::UnpackableBorrowed<#borrow_lt>>::unpack_with_ofs(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
t
})?;
}
}
fn impl_struct_named_borrowed(
name: &Ident,
f: FieldsNamed,
generics: &Generics,
with_packable: bool,
) -> TokenStream2 {
let (borrow_lt, maybe_modified) = resolve_borrow_lifetime(generics);
let impl_gen_src = maybe_modified.as_ref().unwrap_or(generics);
let mut pack_stmts = Vec::new();
let mut unpack_stmts = Vec::new();
let mut field_names = Vec::new();
for field in f.named {
let ident = field.ident.as_ref().cloned().unwrap();
if with_packable {
pack_stmts.push(named_field_stmts(&ident, &field).0);
}
unpack_stmts.push(named_field_borrowed_stmt(&ident, &field, &borrow_lt));
field_names.push(ident);
}
emit_borrowed_struct_impl(
name,
generics,
&borrow_lt,
impl_gen_src,
with_packable.then_some(&pack_stmts[..]),
&unpack_stmts,
quote! { Self { #(#field_names),* } },
)
}
fn impl_struct_unnamed_borrowed(
name: &Ident,
f: FieldsUnnamed,
generics: &Generics,
with_packable: bool,
) -> TokenStream2 {
let (borrow_lt, maybe_modified) = resolve_borrow_lifetime(generics);
let impl_gen_src = maybe_modified.as_ref().unwrap_or(generics);
let mut pack_stmts = Vec::new();
let mut unpack_stmts = Vec::new();
let mut var_names = Vec::new();
for (i, field) in f.unnamed.into_iter().enumerate() {
if has_attr(&field, "map") {
todo!("unnamed map is not implemented for derive macro; implement the traits manually");
}
if has_attr(&field, "array") {
todo!(
"unnamed array is not implemented for derive macro; implement the traits manually"
);
}
let var = format_ident!("v{}", i);
let index = syn::Index::from(i);
if has_attr(&field, "binary") {
if with_packable {
if is_option_type(&field.ty) {
pack_stmts.push(quote! {
n += ::msgpacker::pack_bytes_option(
buf,
self.#index.as_ref().map(::core::convert::AsRef::as_ref),
);
});
} else {
pack_stmts.push(quote! {
n += ::msgpacker::pack_bytes(buf, ::core::convert::AsRef::<[u8]>::as_ref(&self.#index));
});
}
}
if is_option_type(&field.ty) {
unpack_stmts.push(quote! {
let #var = ::msgpacker::unpack_bytes_option_ref(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
t.map(::core::convert::From::from)
})?;
});
} else {
unpack_stmts.push(quote! {
let #var = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
::core::convert::From::from(t)
})?;
});
}
var_names.push(var);
continue;
}
if with_packable {
pack_stmts.push(if is_vec_of_u8(&field.ty) {
quote! { n += ::msgpacker::pack_bytes(buf, self.#index.as_slice()); }
} else if is_slice_of_u8(&field.ty) {
quote! { n += ::msgpacker::pack_bytes(buf, self.#index); }
} else {
quote! { n += self.#index.pack(buf); }
});
}
unpack_stmts.push(quote! {
let #var = <_ as ::msgpacker::UnpackableBorrowed<#borrow_lt>>::unpack_with_ofs(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
t
})?;
});
var_names.push(var);
}
emit_borrowed_struct_impl(
name,
generics,
&borrow_lt,
impl_gen_src,
with_packable.then_some(&pack_stmts[..]),
&unpack_stmts,
quote! { Self(#(#var_names),*) },
)
}
fn impl_struct_unit_borrowed(
name: &Ident,
generics: &Generics,
with_packable: bool,
) -> TokenStream2 {
let (borrow_lt, maybe_modified) = resolve_borrow_lifetime(generics);
let impl_gen_src = maybe_modified.as_ref().unwrap_or(generics);
let (pack_impl_generics, ty_generics, pack_where_clause) = generics.split_for_impl();
let (impl_generics, _, where_clause) = impl_gen_src.split_for_impl();
let packable_impl = with_packable.then(|| quote! {
impl #pack_impl_generics ::msgpacker::Packable for #name #ty_generics #pack_where_clause {
fn pack<T>(&self, _buf: &mut T) -> usize
where
T: ::msgpacker::BufMut,
{
0
}
}
});
quote! {
#packable_impl
impl #impl_generics ::msgpacker::UnpackableBorrowed<#borrow_lt> for #name #ty_generics #where_clause {
type Error = ::msgpacker::Error;
fn unpack_with_ofs(_buf: &#borrow_lt [u8]) -> Result<(usize, Self), Self::Error> {
Ok((0, Self))
}
}
}
}
fn impl_enum_borrowed(
name: &Ident,
data: DataEnum,
generics: &Generics,
with_packable: bool,
) -> TokenStream2 {
if data.variants.is_empty() {
todo!("empty enum is not implemented for derive macro; implement the traits manually");
}
let (borrow_lt, maybe_modified) = resolve_borrow_lifetime(generics);
let impl_gen_src = maybe_modified.as_ref().unwrap_or(generics);
let (pack_impl_generics, ty_generics, pack_where_clause) = generics.split_for_impl();
let (impl_generics, _, where_clause) = impl_gen_src.split_for_impl();
let mut pack_arms = Vec::new();
let mut unpack_arms = Vec::new();
for (i, variant) in data.variants.into_iter().enumerate() {
let discriminant = variant
.discriminant
.map(|(_, d)| d)
.unwrap_or_else(|| syn::parse_str(&i.to_string()).unwrap());
let variant_ident = &variant.ident;
match variant.fields {
Fields::Named(f) => {
let mut field_names = Vec::new();
let mut pack_field_stmts = Vec::new();
let mut unpack_field_stmts = Vec::new();
for field in &f.named {
let ident = field.ident.as_ref().unwrap();
field_names.push(ident.clone());
if has_attr(field, "binary") {
if with_packable {
if is_option_type(&field.ty) {
pack_field_stmts.push(quote! {
n += ::msgpacker::pack_bytes_option(
buf,
#ident.as_ref().map(::core::convert::AsRef::as_ref),
);
});
} else {
pack_field_stmts.push(quote! {
n += ::msgpacker::pack_bytes(buf, ::core::convert::AsRef::<[u8]>::as_ref(#ident));
});
}
}
if is_option_type(&field.ty) {
unpack_field_stmts.push(quote! {
let #ident = ::msgpacker::unpack_bytes_option_ref(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
t.map(::core::convert::From::from)
})?;
});
} else {
unpack_field_stmts.push(quote! {
let #ident = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
::core::convert::From::from(t)
})?;
});
}
} else {
if with_packable {
pack_field_stmts.push(if is_vec_of_u8(&field.ty) {
quote! { n += ::msgpacker::pack_bytes(buf, #ident.as_slice()); }
} else if is_slice_of_u8(&field.ty) {
quote! { n += ::msgpacker::pack_bytes(buf, #ident); }
} else {
quote! { n += #ident.pack(buf); }
});
}
unpack_field_stmts.push(quote! {
let #ident = <_ as ::msgpacker::UnpackableBorrowed<#borrow_lt>>::unpack_with_ofs(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
t
})?;
});
}
}
if with_packable {
pack_arms.push(quote! {
#name::#variant_ident { #(#field_names),* } => {
n += <u32 as ::msgpacker::Packable>::pack(&(#discriminant as u32), buf);
#(#pack_field_stmts)*
}
});
}
unpack_arms.push(quote! {
#discriminant => {
#(#unpack_field_stmts)*
slf = #name::#variant_ident { #(#field_names),* };
}
});
}
Fields::Unnamed(f) => {
let vars: Vec<Ident> = (0..f.unnamed.len())
.map(|j| format_ident!("t{}", j))
.collect();
let mut pack_field_stmts = Vec::new();
let mut unpack_field_stmts = Vec::new();
for (var, field) in vars.iter().zip(f.unnamed.iter()) {
if has_attr(field, "binary") {
if with_packable {
if is_option_type(&field.ty) {
pack_field_stmts.push(quote! {
n += ::msgpacker::pack_bytes_option(
buf,
#var.as_ref().map(::core::convert::AsRef::as_ref),
);
});
} else {
pack_field_stmts.push(quote! {
n += ::msgpacker::pack_bytes(buf, ::core::convert::AsRef::<[u8]>::as_ref(#var));
});
}
}
if is_option_type(&field.ty) {
unpack_field_stmts.push(quote! {
let #var = ::msgpacker::unpack_bytes_option_ref(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
t.map(::core::convert::From::from)
})?;
});
} else {
unpack_field_stmts.push(quote! {
let #var = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
::core::convert::From::from(t)
})?;
});
}
} else {
if with_packable {
pack_field_stmts.push(if is_vec_of_u8(&field.ty) {
quote! { n += ::msgpacker::pack_bytes(buf, #var.as_slice()); }
} else if is_slice_of_u8(&field.ty) {
quote! { n += ::msgpacker::pack_bytes(buf, #var); }
} else {
quote! { n += #var.pack(buf); }
});
}
unpack_field_stmts.push(quote! {
let #var = <_ as ::msgpacker::UnpackableBorrowed<#borrow_lt>>::unpack_with_ofs(buf).map(|(nv, t)| {
n += nv;
buf = &buf[nv..];
t
})?;
});
}
}
if with_packable {
pack_arms.push(quote! {
#name::#variant_ident(#(#vars),*) => {
n += <u32 as ::msgpacker::Packable>::pack(&(#discriminant as u32), buf);
#(#pack_field_stmts)*
}
});
}
unpack_arms.push(quote! {
#discriminant => {
#(#unpack_field_stmts)*
slf = #name::#variant_ident(#(#vars),*);
}
});
}
Fields::Unit => {
if with_packable {
pack_arms.push(quote! {
#name::#variant_ident => {
n += <u32 as ::msgpacker::Packable>::pack(&(#discriminant as u32), buf);
}
});
}
unpack_arms.push(quote! {
#discriminant => slf = #name::#variant_ident,
});
}
}
}
let packable_impl = with_packable.then(|| quote! {
impl #pack_impl_generics ::msgpacker::Packable for #name #ty_generics #pack_where_clause {
fn pack<T>(&self, buf: &mut T) -> usize
where
T: ::msgpacker::BufMut,
{
use ::msgpacker::Packable as _;
let mut n = 0usize;
match self {
#(#pack_arms)*
}
n
}
}
});
quote! {
#packable_impl
impl #impl_generics ::msgpacker::UnpackableBorrowed<#borrow_lt> for #name #ty_generics #where_clause {
type Error = ::msgpacker::Error;
#[allow(unused_mut)]
fn unpack_with_ofs(mut buf: &#borrow_lt [u8]) -> Result<(usize, Self), Self::Error> {
let (mut n, discriminant) =
<u32 as ::msgpacker::Unpackable>::unpack_with_ofs(buf)?;
buf = &buf[n..];
let slf;
match discriminant {
#(#unpack_arms)*
_ => return Err(::msgpacker::Error::InvalidEnumVariant),
}
Ok((n, slf))
}
}
}
}
fn dispatch_borrowed(input: DeriveInput, with_packable: bool) -> TokenStream2 {
let name = &input.ident;
let generics = &input.generics;
match input.data {
Data::Struct(DataStruct {
fields: Fields::Named(f),
..
}) => impl_struct_named_borrowed(name, f, generics, with_packable),
Data::Struct(DataStruct {
fields: Fields::Unnamed(f),
..
}) => impl_struct_unnamed_borrowed(name, f, generics, with_packable),
Data::Struct(DataStruct {
fields: Fields::Unit,
..
}) => impl_struct_unit_borrowed(name, generics, with_packable),
Data::Enum(data) => impl_enum_borrowed(name, data, generics, with_packable),
Data::Union(_) => {
todo!(
"union support is not implemented for derive macro; implement the traits manually"
)
}
}
}
#[proc_macro_derive(MsgPackerBorrowed, attributes(msgpacker))]
pub fn msg_packer_borrowed(input: TokenStream) -> TokenStream {
dispatch_borrowed(parse_macro_input!(input as DeriveInput), true).into()
}
#[proc_macro_derive(MsgUnpackerBorrowed, attributes(msgpacker))]
pub fn msg_unpacker_borrowed(input: TokenStream) -> TokenStream {
dispatch_borrowed(parse_macro_input!(input as DeriveInput), false).into()
}