flow-record-derive 0.4.10

derive crate for flow-record
Documentation
use std::collections::HashSet;

use proc_macro2::TokenStream;
use quote::format_ident;
use quote::quote;
use syn::Type;

use crate::field_info::FieldInfo;
use crate::record_attributes::RecordAttributes;
use crate::without_lifetimes::WithoutLifetimes;

pub struct DescriptorCode {
    pub hash: TokenStream,
    pub descriptor: TokenStream,
}

pub struct StructInfo {
    name: String,
    fields: Vec<FieldInfo>,
    attrs: RecordAttributes,

    children_types: HashSet<Type>,
}

impl StructInfo {
    pub fn new(name: String, s: syn::DataStruct, attrs: RecordAttributes) -> Self {
        match s.fields {
            syn::Fields::Named(n) => {
                let mut fields = Vec::new();
                let mut children_types = HashSet::new();

                for field in n.named.iter() {
                    let field_type = field.ty.clone().without_lifetimes();
                    let field_type_expr = quote! {
                        <#field_type as ::flow_record::prelude::ToMsgPackValue>::field_type()
                    };
                    fields.push(FieldInfo::new(
                        field.ident.as_ref().unwrap().to_string(),
                        field_type_expr,
                    ));

                    if field
                        .attrs
                        .iter()
                        .any(|attr| attr.path().is_ident("has_descriptor"))
                    {
                        children_types.insert(field_type.clone());
                    }
                }

                Self {
                    name,
                    fields,
                    attrs,
                    children_types,
                }
            }
            _ => Self {
                name,
                fields: vec![],
                attrs,
                children_types: HashSet::new(),
            },
        }
    }

    pub fn descriptor(&self) -> TokenStream {
        let name = &self.name;
        let fields = self.fields.iter().map(|field_info| {
            let field_name = &field_info.name;
            let field_type = &field_info.field_type_expr;
            quote! {
                flow_record::prelude::RecordField::from(
                    (#field_name.into(), (#field_type)))
            }
        });
        quote! {
            flow_record::prelude::RecordDescriptor::new(#name.into(), vec![#(#fields),*])
        }
    }

    pub fn child_descriptors(&self) -> Vec<DescriptorCode> {
        self.children_types.iter().map(|ty| {
            let ty = ty.clone();
            let hash = quote! {
                <#ty as ::flow_record::prelude::ToMsgPackValue>::descriptor_hash()
            };
            let descriptor = quote! {
                <#ty as ::flow_record::prelude::ToMsgPackValue>::descriptor()
            };
            DescriptorCode { hash, descriptor }
        }).collect()
    }

    pub fn descriptor_hash(&self) -> TokenStream {
        let name = &self.name;
        let hasher = quote!(hasher);

        let fields = self.fields.iter().map(|field_info| {
            let field_name = &field_info.name;
            let field_type = &field_info.field_type_expr;
            quote! {
                #hasher.write_all(#field_name.as_bytes()).unwrap();
                #hasher.write_all(#field_type.to_string().as_bytes()).unwrap();
            }
        });

        quote! { {
            use std::io::Write;
            use flow_record::prelude::sha2::Digest;
            let mut #hasher = flow_record::prelude::sha2::Sha256::new();
            #hasher.write_all(#name.as_bytes()).unwrap();
            #(#fields);*
            let hash = #hasher.finalize();
            u32::from_be_bytes(hash[0..4].try_into().unwrap())
        }}
    }

    pub fn values(
        &self,
        from_parameter_name: TokenStream,
    ) -> impl Iterator<Item = TokenStream> + '_ {
        self.fields
            .iter()
            .map(move |field_info| {
                let field_name = format_ident!("{}", field_info.name);
                quote! {
                    {
                        use flow_record::ToMsgPackValue;
                        #from_parameter_name.#field_name.to_msgpack_value()
                    }
                }
            })
            .chain(self.attrs.values())
    }
}