#![deny(
clippy::pedantic,
clippy::nursery,
clippy::cargo,
clippy::unwrap_used,
clippy::expect_used,
clippy::suspicious,
clippy::complexity,
clippy::perf,
clippy::style,
unsafe_code
)]
#![allow(clippy::module_name_repetitions, clippy::option_if_let_else)]
#[macro_use]
extern crate quote;
mod attr;
mod codegen;
mod enums;
use attr::{AttrKind, Attrs};
use codegen::{
decode_pad, encode_pad,
trait_impl::{impl_trait_for, TraitImplType},
};
use proc_macro2::TokenStream;
use syn::{parse_macro_input, spanned::Spanned, Error, Result};
use crate::codegen::enums::{decode_discriminant, encode_discriminant, variant_discriminant};
#[derive(Clone, Copy)]
enum Operation {
Decode,
Encode,
}
#[proc_macro_derive(BitDecode, attributes(bin_proto))]
pub fn decode(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let ast: syn::DeriveInput = parse_macro_input!(input as syn::DeriveInput);
match impl_codec(&ast, Operation::Decode) {
Ok(tokens) => tokens,
Err(e) => e.to_compile_error(),
}
.into()
}
#[proc_macro_derive(BitEncode, attributes(bin_proto))]
pub fn encode(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let ast: syn::DeriveInput = parse_macro_input!(input as syn::DeriveInput);
match impl_codec(&ast, Operation::Encode) {
Ok(tokens) => tokens,
Err(e) => e.to_compile_error(),
}
.into()
}
fn impl_codec(ast: &syn::DeriveInput, codec_type: Operation) -> Result<TokenStream> {
match ast.data {
syn::Data::Struct(ref s) => impl_for_struct(ast, s, codec_type),
syn::Data::Enum(ref e) => impl_for_enum(ast, e, codec_type),
syn::Data::Union(..) => Err(Error::new(
ast.span(),
"bin-proto traits are not derivable on unions",
)),
}
}
fn impl_for_struct(
ast: &syn::DeriveInput,
strukt: &syn::DataStruct,
codec_type: Operation,
) -> Result<TokenStream> {
let attrs = Attrs::parse(ast.attrs.as_slice(), Some(AttrKind::Struct), ast.span())?;
let crate_path = attrs.crate_path();
let ctx_ty = attrs.ctx_ty();
let (impl_body, trait_type) = match codec_type {
Operation::Decode => {
let (decodes, initializers) = codegen::decodes(&crate_path, &strukt.fields)?;
let pad_before = attrs
.pad_before
.as_ref()
.map(|pad| decode_pad(&crate_path, pad));
let pad_after = attrs
.pad_after
.as_ref()
.map(|pad| decode_pad(&crate_path, pad));
let magic = attrs.decode_magic();
(
quote!(
fn decode<__R, __E>(
__io_reader: &mut __R,
__ctx: &mut #ctx_ty,
__tag: (),
) -> #crate_path::Result<Self>
where
__R: #crate_path::BitRead,
__E: #crate_path::Endianness,
{
#pad_before
#magic
#decodes
#pad_after
::core::result::Result::Ok(Self #initializers)
}
),
TraitImplType::Decode,
)
}
Operation::Encode => {
let encodes = codegen::encodes(&crate_path, &strukt.fields, true)?;
let pad_before = attrs
.pad_before
.as_ref()
.map(|pad| encode_pad(&crate_path, pad));
let pad_after = attrs
.pad_after
.as_ref()
.map(|pad| encode_pad(&crate_path, pad));
let magic = attrs.encode_magic();
(
quote!(
fn encode<__W, __E>(
&self,
__io_writer: &mut __W,
__ctx: &mut #ctx_ty,
(): (),
) -> #crate_path::Result<()>
where
__W: #crate_path::BitWrite,
__E: #crate_path::Endianness,
{
#pad_before
#magic
#encodes
#pad_after
::core::result::Result::Ok(())
}
),
TraitImplType::Encode,
)
}
};
impl_trait_for(ast, &impl_body, &trait_type)
}
#[allow(clippy::too_many_lines)]
fn impl_for_enum(
ast: &syn::DeriveInput,
e: &syn::DataEnum,
codec_type: Operation,
) -> Result<TokenStream> {
let plan = enums::Enum::try_new(ast, e)?;
let attrs = Attrs::parse(ast.attrs.as_slice(), Some(AttrKind::Enum), ast.span())?;
let crate_path = attrs.crate_path();
let discriminant_ty = &plan.discriminant_ty;
let ctx_ty = attrs.ctx_ty();
Ok(match codec_type {
Operation::Decode => {
let decode_variant = codegen::enums::decode_variant_fields(&plan)?;
let impl_body = quote!(
fn decode<__R, __E>(
__io_reader: &mut __R,
__ctx: &mut #ctx_ty,
__tag: #crate_path::Tag<__Tag>,
) -> #crate_path::Result<Self>
where
__R: #crate_path::BitRead,
__E: #crate_path::Endianness,
{
::core::result::Result::Ok(#decode_variant)
}
);
let tagged_decode_impl = impl_trait_for(
ast,
&impl_body,
&TraitImplType::TaggedDecode(discriminant_ty.clone()),
)?;
let decode_discriminant = decode_discriminant(&attrs);
let impl_body = quote!(
fn decode<__R, __E>(
__io_reader: &mut __R,
__ctx: &mut #ctx_ty,
__tag: (),
) -> #crate_path::Result<Self>
where
__R: #crate_path::BitRead,
__E: #crate_path::Endianness,
{
let __tag: #discriminant_ty = #decode_discriminant?;
<Self as #crate_path::BitDecode<_, #crate_path::Tag<#discriminant_ty>>>::decode::<_, __E>(
__io_reader,
__ctx,
#crate_path::Tag(__tag)
)
}
);
let decode_impl = impl_trait_for(ast, &impl_body, &TraitImplType::Decode)?;
quote!(
#tagged_decode_impl
#decode_impl
)
}
Operation::Encode => {
let encode_variant = codegen::enums::encode_variant_fields(&plan)?;
let pad_before = attrs
.pad_before
.as_ref()
.map(|pad| encode_pad(&crate_path, pad));
let pad_after = attrs
.pad_after
.as_ref()
.map(|pad| encode_pad(&crate_path, pad));
let impl_body = quote!(
fn encode<__W, __E>(
&self,
__io_writer: &mut __W,
__ctx: &mut #ctx_ty,
__tag: #crate_path::Untagged,
) -> #crate_path::Result<()>
where
__W: #crate_path::BitWrite,
__E: #crate_path::Endianness,
{
#pad_before
#encode_variant
#pad_after
::core::result::Result::Ok(())
}
);
let untagged_encode_impl =
impl_trait_for(ast, &impl_body, &TraitImplType::UntaggedEncode)?;
let variant_discriminant = variant_discriminant(&plan)?;
let impl_body = quote!(
type Discriminant = #discriminant_ty;
fn discriminant(&self) -> ::core::option::Option<Self::Discriminant> {
#variant_discriminant
}
);
let discriminable_impl =
impl_trait_for(ast, &impl_body, &TraitImplType::Discriminable)?;
let encode_discriminant = encode_discriminant(&attrs);
let impl_body = quote!(
fn encode<__W, __E>(
&self,
__io_writer: &mut __W,
__ctx: &mut #ctx_ty,
(): (),
) -> #crate_path::Result<()>
where
__W: #crate_path::BitWrite,
__E: #crate_path::Endianness,
{
#pad_before
#encode_discriminant
let res = <Self as #crate_path::BitEncode<_, _>>::encode::<_, __E>(
self,
__io_writer,
__ctx,
#crate_path::Untagged
)?;
#pad_after
::core::result::Result::Ok(res)
}
);
let encode_impl = impl_trait_for(ast, &impl_body, &TraitImplType::Encode)?;
quote!(
#untagged_encode_impl
#discriminable_impl
#encode_impl
)
}
})
}