postro-macros 0.1.1

Postro Proc Macro
Documentation
use proc_macro::TokenStream;
use quote::quote;
use syn::*;

macro_rules! error {
    ($($tt:tt)*) => {
        return Err(syn::Error::new(proc_macro::Span::call_site().into(), format!($($tt)*)))
    };
}

pub fn from_row(input: DeriveInput) -> Result<TokenStream> {
    let DeriveInput { attrs: _, vis: _, ident, mut generics, data } = input;
    let Data::Struct(data) = data else {
        error!("only struct are currently supported")
    };

    let body = match data.fields {
        Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
            let iter = (0..unnamed.len())
                .map(|_|quote! { iter.try_next()?.decode()?, });

            quote! {
                use ::postro::DecodeError::IndexOutOfBounds as Nope;
                let mut iter = row.into_iter();
                Ok(Self(#(#iter)*))
            }
        },
        Fields::Named(FieldsNamed { named, .. }) => {
            let vars = named
                .iter()
                .map(|e|e.ident.as_ref().unwrap())
                .map(|e|(e.to_string(),e))
                .map(|(name,id)|quote! { let mut #id = Err(Nope(#name.into())); });
            let arms = named
                .iter()
                .map(|e|e.ident.as_ref().unwrap())
                .map(|e|(e.to_string(),e))
                .map(|(name,id)| quote! { #name => #id = Ok(col.decode()?), });
            let iter = named
                .iter()
                .map(|e|e.ident.as_ref().unwrap())
                .map(|id|quote! { #id: #id?, });

            quote! {
                use ::postro::DecodeError::ColumnNotFound as Nope;
                #(#vars)*
                for column in row {
                    let col = column?;
                    match col.name() {
                        #(#arms)*
                        _ => {}
                    }
                }
                Ok(Self {
                    #(#iter)*
                })
            }
        }
        Fields::Unit => quote! {
            Ok(Self)
        }
    };

    for ty in generics.type_params_mut() {
        ty.bounds.push(syn::parse_quote!(::postro::Decode));
    }

    let (g1, g2, g3) = generics.split_for_impl();

    Ok(quote! {
        impl #g1 ::postro::FromRow for #ident #g2 #g3 {
            fn from_row(row: ::postro::Row) -> Result<Self, ::postro::DecodeError> {
                #body
            }
        }
    }.into())
}