armour-derive 0.27.3

DDL and serialization for key-value storage
Documentation
use darling::FromField;
use proc_macro2::TokenStream;
use quote::{ToTokens, quote};
use syn::{DataStruct, Field, Fields, Ident};

use crate::{CustomField, FieldAttr};

struct FieldData {
    field: Field,
    idx: u32,
    attr: FieldAttr,
}

pub fn parse(data: &DataStruct, name: Ident) -> TokenStream {
    let string_name = name.to_string();

    match &data.fields {
        Fields::Named(fields) => {
            let named = &fields.named;
            let named_len = named.len();
            let mut fields_insert: Vec<FieldData> = Vec::with_capacity(named_len);

            let mut seq = 0u32;

            for field in named.iter() {
                let attr = match FieldAttr::from_field(field) {
                    Ok(attr) => attr,
                    Err(err) => return err.write_errors(),
                };
                let idx = attr.idx.unwrap_or_else(|| {
                    let current_seq = seq;
                    seq += 1;
                    current_seq
                });

                let f = FieldData {
                    field: field.clone(),
                    idx,
                    attr,
                };

                fields_insert.push(f);
            }

            fields_insert.sort_by_key(|f| f.idx);

            let fields_stream: Vec<TokenStream> = fields_insert
                .iter()
                .map(|f| {
                    let ident = f.field.ident.as_ref().unwrap().to_string();
                    let ty = &f.field.ty;

                    let typ;
                    if let Some(with_type) = &f.attr.with_type {
                        typ = with_type.to_token_stream();
                    } else if f.attr.unimplemented.is_present() {
                        typ = quote! {
                            ::armour_core::Typ::Custom("unimplemented", &[])
                        };
                    } else if let Some(CustomField { name, types }) = &f.attr.custom {
                        typ = quote! {
                            ::armour_core::Typ::Custom(#name, #types)
                        };
                    } else {
                        typ = quote! {
                            <#ty as ::armour_core::dyn_types::get_type::GetType>::TYPE
                        };
                    }

                    quote! {
                        (#ident, #typ),
                    }
                })
                .collect();

            let res = quote! {
                impl ::armour_core::dyn_types::get_type::GetType for #name {
                    const TYPE: ::armour_core::Typ = {
                        let fields = &[
                            #(#fields_stream)*
                        ];

                        ::armour_core::Typ::Struct(::armour_core::dyn_types::StructType {
                            name: #string_name,
                            fields: ::armour_core::dyn_types::Fields::Named(fields),
                        })
                    };
                }
            };

            res
        }
        Fields::Unnamed(fields) => {
            let unnamed = &fields.unnamed;

            if unnamed.len() == 1 {
                let field = &unnamed[0];
                let attr = match FieldAttr::from_field(field) {
                    Ok(attr) => attr,
                    Err(err) => return err.write_errors(),
                };
                let is_flatten = attr.flatten.is_present();
                if is_flatten {
                    let typ = &field.ty;

                    let res = quote! {
                        impl ::armour_core::dyn_types::get_type::GetType for #name {
                            const TYPE: ::armour_core::Typ = <#typ as ::armour_core::dyn_types::get_type::GetType>::TYPE;
                        }
                    };

                    return res;
                }
            }

            let fields_stream: Vec<TokenStream> = unnamed
                .iter()
                .map(|field| {
                    let typ = &field.ty;

                    quote! {
                        <#typ as ::armour_core::dyn_types::get_type::GetType>::TYPE,
                    }
                })
                .collect();

            let res = quote! {
                impl ::armour_core::dyn_types::get_type::GetType for #name {
                    const TYPE: ::armour_core::Typ = {
                        let fields = &[
                            #(#fields_stream)*
                        ];

                        ::armour_core::Typ::Struct(::armour_core::dyn_types::StructType {
                            name: #string_name,
                            fields: ::armour_core::dyn_types::Fields::Unnamed(fields),
                        })
                    };
                }
            };

            res
        }
        Fields::Unit => {
            let res = quote! {
                impl ::armour_core::dyn_types::get_type::GetType for #name {
                    const TYPE: ::armour_core::Typ = ::armour_core::Typ::Void;
                }
            };

            res
        }
    }
}