tinycbor-derive 0.15.2

Derive tinycbor traits.
Documentation
use proc_macro2::TokenStream;
use quote::quote;

use crate::ast::{Field, parse_fields};

pub struct Variant {
    pub tag: u64,
    pub ident: syn::Ident,
    pub fields: Vec<Field>,
}

impl Variant {
    pub fn parse(input: &syn::Variant, generics: &syn::Generics) -> syn::Result<Self> {
        let mut index = None;

        for attr in &input.attrs {
            if super::attr_index(&mut index, attr)? {
                continue;
            }
            if !attr.path().is_ident("cbor") {
                continue;
            }
            attr.parse_nested_meta(|meta| {
                if super::meta_index(&mut index, &meta)? {
                    return Ok(());
                }
                Err(meta.error("unknown attribute"))
            })?;
        }

        Ok(Variant {
            tag: index.ok_or_else(|| syn::Error::new_spanned(input, "missing index attribute"))?,
            ident: input.ident.clone(),
            fields: parse_fields(&input.fields, generics)?,
        })
    }

    pub fn decode(self, naked: bool, single_field: bool) -> TokenStream {
        let Variant { tag, ident, fields } = self;
        let variant_name = &ident;

        let field_count = fields.len();
        let fields = fields.into_iter().map(|f| {
            let error_constructor = if field_count == 1 {
                if single_field {
                    quote! { ::tinycbor::tag::Error::Content(e) }
                } else {
                    quote! { ::tinycbor::tag::Error::Content(__Error::#variant_name(e)) }
                }
            } else {
                let error_name = f.error_name();
                let ident = quote::format_ident!("{}{}", variant_name, error_name);
                quote! { ::tinycbor::tag::Error::Content(__Error::#ident(e)) }
            };

            f.decode(&error_constructor, naked)
        });

        quote! {
            #tag => {
                Self::#ident {
                    #(#fields),*
                }
            }
        }
    }

    pub fn encode(self, naked: bool) -> TokenStream {
        let Variant { tag, ident, fields } = self;
        let variant_name = &ident;
        let field_count = fields.len();

        let destruct: TokenStream = fields.iter().map(|f| f.destruct()).collect();
        let procedures = fields.into_iter().map(|f| f.encode());
        let container = (!naked).then(|| quote! { e.array(#field_count + 1)?; });

        quote! {
            Self::#variant_name { #destruct } => {
                #container
                <u64 as ::tinycbor::Encode>::encode(&#tag, e)?;
                #(#procedures)*
                Ok(())
            }
        }
    }

    pub fn len(self, naked: bool) -> TokenStream {
        let Variant { tag, ident, fields } = self;
        let variant_name = &ident;
        let field_count = fields.len();

        let destructors: TokenStream = fields.iter().map(|f| f.destruct()).collect();
        let field_lens = fields.into_iter().map(|f| f.len());
        let container_len = (!naked).then(|| {
            quote! {
                total_len += <usize as ::tinycbor::CborLen>::cbor_len(&(#field_count + 1));
            }
        });
        quote! {
            Self::#variant_name { #destructors } => {
                let mut total_len = <u64 as ::tinycbor::CborLen>::cbor_len(&#tag);
                #container_len
                #(total_len += #field_lens;)*
                total_len
            }
        }
    }
}