bin-proto-derive 0.12.3

Derive macros for bin-proto
Documentation
pub mod enums;
pub mod trait_impl;

use crate::attr::{AttrKind, Attrs, Tag};
use proc_macro2::TokenStream;
use syn::{spanned::Spanned, Error, Result};

pub fn decodes(
    crate_path: &TokenStream,
    fields: &syn::Fields,
) -> Result<(TokenStream, TokenStream)> {
    match fields {
        syn::Fields::Named(fields) => decode_named_fields(crate_path, fields),
        syn::Fields::Unnamed(fields) => Ok((
            TokenStream::new(),
            decode_unnamed_fields(crate_path, fields)?,
        )),
        syn::Fields::Unit => Ok((TokenStream::new(), TokenStream::new())),
    }
}

pub fn encodes(
    crate_path: &TokenStream,
    fields: &syn::Fields,
    self_prefix: bool,
) -> Result<TokenStream> {
    match fields {
        syn::Fields::Named(fields) => encode_named_fields(crate_path, fields, self_prefix),
        syn::Fields::Unnamed(fields) => encode_unnamed_fields(crate_path, fields, self_prefix),
        syn::Fields::Unit => Ok(TokenStream::new()),
    }
}

fn decode_named_fields(
    crate_path: &TokenStream,
    fields_named: &syn::FieldsNamed,
) -> Result<(TokenStream, TokenStream)> {
    let fields = fields_named
        .named
        .iter()
        .map(|field| {
            let field_name = &field.ident;
            let field_ty = &field.ty;

            let decode = decode(crate_path, field)?;

            Ok(quote!(
                let #field_name : #field_ty = #decode;
            ))
        })
        .collect::<Result<Vec<_>>>()?;

    let field_initializers: Vec<_> = fields_named
        .named
        .iter()
        .map(|field| {
            let field_name = &field.ident;

            quote!(#field_name)
        })
        .collect();

    Ok((
        quote!( #( #fields )* ),
        quote!( { #( #field_initializers ),* } ),
    ))
}

pub fn decode_pad(crate_path: &TokenStream, pad: &syn::Expr) -> TokenStream {
    quote!(#crate_path::BitRead::skip(__io_reader, #pad)?;)
}

fn decode(crate_path: &TokenStream, field: &syn::Field) -> Result<TokenStream> {
    let attrs = Attrs::parse(field.attrs.as_slice(), Some(AttrKind::Field), field.span())?;

    if attrs.skip_decode {
        return Ok(quote!(::core::default::Default::default()));
    }

    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();

    let decode = if let Some(Tag::Prepend { typ, bits, .. }) = attrs.tag {
        let tag = if let Some(bits) = bits {
            quote!(#crate_path::Bits::<#bits>)
        } else {
            quote!(())
        };
        quote!({
            let __tag: #typ = #crate_path::BitDecode::decode::<_, __E>(__io_reader, __ctx, #tag)?;
            #crate_path::BitDecode::decode::<_, __E>(
                __io_reader,
                __ctx,
                #crate_path::Tag(__tag)
            )?
        })
    } else {
        let tag = if let Some(field_width) = attrs.bits {
            quote!(#crate_path::Bits::<#field_width>)
        } else if attrs.untagged {
            quote!(#crate_path::Untagged)
        } else if let Some(Tag::External(tag)) = attrs.tag {
            quote!(#crate_path::Tag(#tag))
        } else {
            quote!(())
        };
        quote!(#crate_path::BitDecode::decode::<_, __E>(__io_reader, __ctx, #tag)?)
    };

    Ok(quote!({
        #pad_before
        #magic
        let decoded = #decode;
        #pad_after
        decoded
    }))
}

pub fn encode_pad(crate_path: &TokenStream, pad: &syn::Expr) -> TokenStream {
    quote!(#crate_path::BitWrite::pad(__io_writer, #pad)?;)
}

fn encode(
    crate_path: &TokenStream,
    field: &syn::Field,
    field_name: &TokenStream,
) -> Result<TokenStream> {
    let attrs = Attrs::parse(field.attrs.as_slice(), Some(AttrKind::Field), field.span())?;

    if attrs.skip_encode {
        return Ok(TokenStream::new());
    }

    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();

    let field_ref = if let Some(value) = attrs.write_value {
        let ty = &field.ty;
        quote!(&{
            let value: #ty = {#value};
            value
        })
    } else {
        field_name.clone()
    };

    let encode = if let Some(Tag::Prepend {
        typ,
        write_value,
        bits,
    }) = attrs.tag
    {
        let Some(write_value) = write_value else {
            return Err(Error::new(field.span(), "Tag must specify 'write_value'"));
        };
        let tag = if let Some(bits) = bits {
            quote!(#crate_path::Bits::<#bits>)
        } else {
            quote!(())
        };
        quote!(
            {
                <#typ as #crate_path::BitEncode::<_, _>>::encode::<_, __E>(
                    &{#write_value},
                    __io_writer,
                    __ctx,
                    #tag
                )?;
                #crate_path::BitEncode::encode::<_, __E>(
                    #field_ref,
                    __io_writer,
                    __ctx,
                    #crate_path::Untagged
                )?
            }
        )
    } else {
        let tag = if let Some(field_width) = attrs.bits {
            quote!(#crate_path::Bits::<#field_width>)
        } else if matches!(attrs.tag, Some(Tag::External(_))) || attrs.untagged {
            quote!(#crate_path::Untagged)
        } else {
            quote!(())
        };
        quote!(
            {
                #crate_path::BitEncode::encode::<_, __E>(#field_ref, __io_writer, __ctx, #tag)?
            }
        )
    };

    Ok(quote!(
        #pad_before
        #magic
        #encode;
        #pad_after
    ))
}

fn encode_named_fields(
    crate_path: &TokenStream,
    fields_named: &syn::FieldsNamed,
    self_prefix: bool,
) -> Result<TokenStream> {
    let field_encoders = fields_named
        .named
        .iter()
        .map(|field| {
            let field_name = &field.ident;
            encode(
                crate_path,
                field,
                &if self_prefix {
                    quote!(&self. #field_name)
                } else {
                    quote!(#field_name)
                },
            )
        })
        .collect::<Result<Vec<_>>>()?;

    Ok(quote!( #( #field_encoders )* ))
}

fn decode_unnamed_fields(
    crate_path: &TokenStream,
    fields_unnamed: &syn::FieldsUnnamed,
) -> Result<TokenStream> {
    let field_initializers = fields_unnamed
        .unnamed
        .iter()
        .map(|field| {
            let field_ty = &field.ty;
            let decode = decode(crate_path, field)?;

            Ok(quote!(
                {
                    let res: #field_ty = #decode;
                    res
                }
            ))
        })
        .collect::<Result<Vec<_>>>()?;

    Ok(quote!( ( #( #field_initializers ),* ) ))
}

fn encode_unnamed_fields(
    crate_path: &TokenStream,
    fields_unnamed: &syn::FieldsUnnamed,
    self_prefix: bool,
) -> Result<TokenStream> {
    let field_encoders: Vec<_> = fields_unnamed
        .unnamed
        .iter()
        .enumerate()
        .map(|(field_index, field)| {
            let field_index = syn::Index::from(field_index);
            encode(
                crate_path,
                field,
                &if self_prefix {
                    quote!(&self. #field_index)
                } else {
                    format!("field_{}", field_index.index).parse()?
                },
            )
        })
        .collect::<Result<Vec<_>>>()?;

    Ok(quote!( #( #field_encoders )* ))
}