bytey_derive 0.2.0

Bytey derive macros, use Bytey instead
Documentation
use crate::source::{EnumSource, StructSource};
use quote::quote;

pub fn expand_derive_byte_buffer_read(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
    match &input.data {
        syn::Data::Struct(_) => {
            let source = StructSource::from_input(input);

            handle_struct(source)
        }
        syn::Data::Enum(_) => {
            let source = EnumSource::from_input(input);

            handle_enum(source)
        }
        syn::Data::Union(_) => {
            input
                .ident
                .span()
                .unwrap()
                .error("Unions are currently not supported")
                .emit();

            proc_macro2::TokenStream::new()
        }
    }
}

fn handle_struct(input: StructSource) -> proc_macro2::TokenStream {
    let struct_ident = input.ident;
    let init_struct_native: proc_macro2::TokenStream;
    let init_struct_le: proc_macro2::TokenStream;
    let init_struct_be: proc_macro2::TokenStream;

    match input.fields {
        syn::Fields::Named(syn::FieldsNamed { named, .. }) => {
            let mut field_reads_native: Vec<proc_macro2::TokenStream> = Vec::new();
            let mut field_reads_le: Vec<proc_macro2::TokenStream> = Vec::new();
            let mut field_reads_be: Vec<proc_macro2::TokenStream> = Vec::new();

            for field in named {
                let field_ident = field.ident.as_ref().unwrap();
                let field_ty = &field.ty;

                field_reads_native.push(quote! {
                    #field_ident: buffer.read::<#field_ty>()?
                });

                field_reads_le.push(quote! {
                    #field_ident: buffer.read_le::<#field_ty>()?
                });

                field_reads_be.push(quote! {
                    #field_ident: buffer.read_be::<#field_ty>()?
                });
            }

            init_struct_native = quote! {
                #struct_ident {
                    #(#field_reads_native),*
                }
            };

            init_struct_le = quote! {
                #struct_ident {
                    #(#field_reads_le),*
                }
            };

            init_struct_be = quote! {
                #struct_ident {
                    #(#field_reads_be),*
                }
            };
        }
        syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed, .. }) => {
            let mut field_reads_native: Vec<proc_macro2::TokenStream> = Vec::new();
            let mut field_reads_le: Vec<proc_macro2::TokenStream> = Vec::new();
            let mut field_reads_be: Vec<proc_macro2::TokenStream> = Vec::new();

            for field in unnamed {
                let field_ty = &field.ty;

                field_reads_native.push(quote! {
                    buffer.read::<#field_ty>()?
                });

                field_reads_le.push(quote! {
                    buffer.read_le::<#field_ty>()?
                });

                field_reads_be.push(quote! {
                    buffer.read_be::<#field_ty>()?
                });
            }

            init_struct_native = quote! {
                #struct_ident (
                    #(#field_reads_native),*
                )
            };

            init_struct_le = quote! {
                #struct_ident (
                    #(#field_reads_le),*
                )
            };

            init_struct_be = quote! {
                #struct_ident (
                    #(#field_reads_be),*
                )
            };
        }
        syn::Fields::Unit => {
            input
                .ident
                .span()
                .unwrap()
                .error("Unit structs are currently not supported")
                .emit();

            return proc_macro2::TokenStream::new();
        }
    }

    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

    quote! {
        impl #impl_generics ::bytey::ByteBufferRead for #struct_ident #ty_generics #where_clause {
            #[inline]
            fn read_from_buffer(buffer: &mut ::bytey::ByteBuffer) -> ::bytey::Result<#struct_ident #ty_generics> {
                Ok(#init_struct_native)
            }

            #[inline]
            fn read_from_buffer_le(buffer: &mut ::bytey::ByteBuffer) -> ::bytey::Result<#struct_ident #ty_generics> {
                Ok(#init_struct_le)
            }

            #[inline]
            fn read_from_buffer_be(buffer: &mut ::bytey::ByteBuffer) -> ::bytey::Result<#struct_ident #ty_generics> {
                Ok(#init_struct_be)
            }
        }
    }
}

fn handle_enum(input: EnumSource) -> proc_macro2::TokenStream {
    let enum_ident = input.ident;
    let mut match_arms_native: Vec<proc_macro2::TokenStream> = Vec::new();
    let mut match_arms_le: Vec<proc_macro2::TokenStream> = Vec::new();
    let mut match_arms_be: Vec<proc_macro2::TokenStream> = Vec::new();
    let mut id: u16 = 1;

    for variant in &input.variants {
        let variant_ident = variant.ident;

        match variant.fields {
            syn::Fields::Named(syn::FieldsNamed { named, .. }) => {
                let mut field_idents: Vec<&syn::Ident> = Vec::new();
                let mut field_tys: Vec<&syn::Type> = Vec::new();

                for field in named {
                    field_idents.push(field.ident.as_ref().unwrap());
                    field_tys.push(&field.ty);
                }

                match_arms_native.push(quote! {
                    #id => {
                        Ok(#enum_ident::#variant_ident {
                            #( #field_idents: buffer.read::<#field_tys>()? ),*
                        })
                    }
                });

                match_arms_le.push(quote! {
                    #id => {
                        Ok(#enum_ident::#variant_ident {
                            #( #field_idents: buffer.read_le::<#field_tys>()? ),*
                        })
                    }
                });

                match_arms_be.push(quote! {
                    #id => {
                        Ok(#enum_ident::#variant_ident {
                            #( #field_idents: buffer.read_be::<#field_tys>()? ),*
                        })
                    }
                });
            }
            syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed, .. }) => {
                let mut field_tys: Vec<&syn::Type> = Vec::new();

                for field in unnamed {
                    field_tys.push(&field.ty);
                }

                match_arms_native.push(quote! {
                    #id => {
                        Ok(#enum_ident::#variant_ident (
                            #( buffer.read::<#field_tys>()? ),*
                        ))
                    }
                });

                match_arms_le.push(quote! {
                    #id => {
                        Ok(#enum_ident::#variant_ident (
                            #( buffer.read_le::<#field_tys>()? ),*
                        ))
                    }
                });

                match_arms_be.push(quote! {
                    #id => {
                        Ok(#enum_ident::#variant_ident (
                            #( buffer.read_be::<#field_tys>()? ),*
                        ))
                    }
                });
            }
            syn::Fields::Unit => {
                match_arms_native.push(quote! {
                    #id => Ok(#enum_ident::#variant_ident)
                });

                match_arms_le.push(quote! {
                    #id => Ok(#enum_ident::#variant_ident)
                });

                match_arms_be.push(quote! {
                    #id => Ok(#enum_ident::#variant_ident)
                });
            }
        }

        id += 1;
    }

    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

    quote! {
        impl #impl_generics ::bytey::ByteBufferRead for #enum_ident #ty_generics #where_clause {
            #[inline]
            fn read_from_buffer(buffer: &mut ::bytey::ByteBuffer) -> ::bytey::Result<#enum_ident #ty_generics> {
                match buffer.read::<u16>()? {
                    #(#match_arms_native,)*
                    id => Err(::bytey::ByteBufferError::OtherError { error: ::std::format!("Invalid id: {}", id) })
                }
            }

            #[inline]
            fn read_from_buffer_le(buffer: &mut ::bytey::ByteBuffer) -> ::bytey::Result<#enum_ident #ty_generics> {
                match buffer.read::<u16>()? {
                    #(#match_arms_le,)*
                    id => Err(::bytey::ByteBufferError::OtherError { error: ::std::format!("Invalid id: {}", id) })
                }
            }

            #[inline]
            fn read_from_buffer_be(buffer: &mut ::bytey::ByteBuffer) -> ::bytey::Result<#enum_ident #ty_generics> {
                match buffer.read::<u16>()? {
                    #(#match_arms_be,)*
                    id => Err(::bytey::ByteBufferError::OtherError { error: ::std::format!("Invalid id: {}", id) })
                }
            }
        }
    }
}