nimble-derive 0.2.0

Async friendly, simple and fast binary encoding/decoding
Documentation
use proc_macro2::TokenStream;
use quote::{quote, quote_spanned};
use syn::{punctuated::Iter, spanned::Spanned, Field, Index};

use crate::{
    context::{Context, ExprType},
    util::{get_variant_pattern_match_expr, FieldExt, FieldsExt, VariantExt},
};

pub trait EncodeToExpr {
    fn encode_to_expr(&self) -> TokenStream;
}

impl<'a> EncodeToExpr for Context<'a> {
    fn encode_to_expr(&self) -> TokenStream {
        let name = &self.name;
        let field_prefix = &self.field_prefix;

        match &self.expr_type {
            ExprType::Struct { ref fields, .. } => {
                bytes_encoding_expr(fields.clone(), &field_prefix, None)
            }
            ExprType::Enum { ref variants } => {
                let match_exprs = variants
                    .clone()
                    .enumerate()
                    .map(|(i, variant)| -> TokenStream {
                        let span = variant.span();
                        let variant_name = variant.get_name();
                        let fields_type = variant.fields.get_type();
                        let fields = &variant.fields;
                        let pattern_matching =
                            get_variant_pattern_match_expr(fields.iter_fields(), fields_type, true);
                        let variant_index = i as u32;
                        let bytes_encoding = bytes_encoding_expr(
                            fields.iter_fields(),
                            &field_prefix,
                            Some(quote! {Encode::encode_to(& #variant_index, config, &mut writer).await?}),
                        );

                        quote_spanned! {span=>
                            #name :: #variant_name #pattern_matching => #bytes_encoding
                        }
                    });

                quote! {
                    match self {
                        #(#match_exprs,)*
                    }
                }
            }
        }
    }
}

fn bytes_encoding_expr(
    fields: Iter<Field>,
    field_prefix: &TokenStream,
    base_expr: Option<TokenStream>,
) -> TokenStream {
    let recurse = fields.enumerate().map(|(i, f)| {
        let field_name = f.get_name();

        match field_name {
            Some(field_name) => quote_spanned! {f.span()=>
                Encode::encode_to(#field_prefix #field_name, config, &mut writer).await?
            },
            None => {
                let index = Index::from(i);
                quote_spanned! {f.span()=>
                    Encode::encode_to(#field_prefix #index, config, &mut writer).await?
                }
            }
        }
    });

    let base_expr = base_expr.unwrap_or_else(|| quote! {0});

    quote! {
        Ok(#base_expr #(+ #recurse)*)
    }
}