streamhouse-derive 0.0.1

The derive macro crate for streamhouse
Documentation
use proc_macro2::{Ident, TokenStream};
use quote::{quote, ToTokens};
use syn::{Data, DataStruct, Fields, Type};

pub(crate) enum RowStruct {
    Named {
        name: Ident,
        field_names: Vec<Ident>,
        field_types: Vec<Type>,
    },
    Unnamed {
        name: Ident,
        field_type: Type,
    },
}

impl RowStruct {
    pub fn parse(name: &Ident, data: &Data) -> Self {
        let name = name.clone();

        match data {
            syn::Data::Struct(DataStruct {
                fields: Fields::Named(fields),
                ..
            }) => RowStruct::Named {
                name,
                field_names: fields.named.iter().flat_map(|f| f.ident.clone()).collect(),
                field_types: fields.named.iter().map(|f| f.ty.clone()).collect(),
            },
            syn::Data::Struct(DataStruct {
                fields: Fields::Unnamed(fields),
                ..
            }) => {
                if fields.unnamed.len() != 1 {
                    panic!("Row can only support a single unnamed field");
                }
                RowStruct::Unnamed {
                    name,
                    field_type: fields.unnamed.first().unwrap().ty.clone(),
                }
            }
            _ => panic!("Row cannot support unit structs"),
        }
    }
}

impl ToTokens for RowStruct {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        match self {
            RowStruct::Named {
                name,
                field_names,
                field_types,
            } => {
                let field_name_strs = field_names
                    .iter()
                    .map(|x| x.to_string())
                    .collect::<Vec<_>>();

                tokens.extend(
                    [quote! {
                        impl ::streamhouse::Row for #name {
                            fn columns(_parent: &'static str) -> Vec<::streamhouse::internal::Column> {
                                let mut out = Vec::new();
                                #(out.extend(<#field_types as ::streamhouse::Row>::columns(#field_name_strs));)*
                                out
                            }
                            fn read(buf: &mut ::streamhouse::internal::Bytes) -> Result<Self, ::streamhouse::Error> {
                                #(let #field_names = buf.read()?;)*
                                Ok(#name { #(#field_names),* })
                            }
                            fn write(&self, buf: &mut impl ::streamhouse::internal::WriteRowBinary) -> Result<(), ::streamhouse::Error> {
                                use ::streamhouse::Row;
                                #(self.#field_names.write(buf)?;)*
                                Ok(())
                            }
                        }
                    }]
                    .into_iter(),
                );
            }
            RowStruct::Unnamed { name, field_type } => {
                tokens.extend(
                    [quote! {
                        impl ::streamhouse::Row for #name {
                            fn columns(parent: &'static str) -> Vec<::streamhouse::internal::Column> {
                                <#field_type as ::streamhouse::Row>::columns(parent)
                            }
                            fn read(buf: &mut ::streamhouse::internal::Bytes) -> Result<Self, ::streamhouse::Error> {
                                Ok(#name ( buf.read()? ))
                            }
                            fn write(&self, buf: &mut impl ::streamhouse::internal::WriteRowBinary) -> Result<(), ::streamhouse::Error> {
                                ::streamhouse::Row::write(&self.0, buf)
                            }
                        }
                    }]
                    .into_iter(),
                );
            }
        }
    }
}