enum-status-code-macros 0.3.0

Macros for enum-status-code
Documentation
extern crate proc_macro;

use heck::AsSnekCase;
use itertools::Itertools;
use manyhow::manyhow;
use proc_macro2::TokenStream;
use quote::{ToTokens, quote};
use syn::{Attribute, Error, Expr, Field, Ident, ItemEnum, parse2, spanned::Spanned};

#[manyhow(proc_macro_attribute)]
pub fn status_code(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
    let mut item: ItemEnum = parse2(item)?;
    let enum_name = &item.ident;
    let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl();
    let enum_span = item.span();

    let default = (!attr.is_empty()).then(|| parse2(attr));
    item.variants
        .iter_mut()
        .map(|variant| {
            let attribute = &variant
                .attrs
                .pop_if(|attr| attr.path().is_ident("code"));
            let attribute = attribute.as_ref();
            if let Some(value) = attribute
                .map(Attribute::parse_args::<Expr>)
                .or(default.clone())
            {
                let variant_name = &variant.ident;
                let mut value = value?.into_token_stream();
                let turbofish = ty_generics.as_turbofish();
                let pattern: Option<TokenStream> = match &variant.fields {
                    syn::Fields::Named(fields) => {
                        fields.named.iter()
                            .map(|Field { ident, .. }| ident.into_token_stream())
                            .reduce(|a, b| quote! {
                                #a, #b
                            })
                            .map(|p| quote! {
                                { #p }
                            })
                    },
                    syn::Fields::Unnamed(fields) => {
                        let variant_name = Ident::new(&AsSnekCase(variant_name.to_string()).to_string(), enum_span);
                        let tuple_members = (0..fields.unnamed.len()).map(|id| {
                            let id_ident = Ident::new(&format!("__tuple_n{}", id), enum_span);
                            id_ident.into_token_stream()
                        }).reduce(|a, b| {
                            quote! {
                                #a, #b
                            }
                        });
                        value = quote! {
                            {
                                let #variant_name = (#tuple_members);
                                #value   
                            }
                        };
                        Some(quote! {
                            (#tuple_members)
                        })
                    },
                    syn::Fields::Unit => None,
                };
                Ok(quote! {
                    #enum_name #turbofish :: #variant_name #pattern => #value,
                })
            } else {
                Err(Error::new(
                    variant.span(),
                    "variant is missing status code and no default set",
                ))
            }
        })
        .process_results(|iter| {
            iter.tree_reduce(|a, b| {
                quote! {
                    #a
                    #b
                }
            })
        })?
        .map(|contents| {
            quote! {
                #item

                impl #impl_generics enum_status_code::AsStatusCode for #enum_name #ty_generics #where_clause {
                    fn as_status_code(&self) -> enum_status_code::http::StatusCode {
                        match self {
                            #contents
                        }
                    }
                }
            }
        })
        .ok_or(Error::new(enum_span, "expcted enum body"))
}