enum-status-code-macros 0.2.0

Macros for enum-status-code
Documentation
extern crate proc_macro;
use itertools::Itertools;
use manyhow::manyhow;
use proc_macro2::TokenStream;
use quote::quote;
use syn::{Attribute, Error, Expr, 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 enum_span = item.span();

    let default = (!attr.is_empty()).then(|| Ok(parse2(attr)?));
    item.variants
        .iter_mut()
        .map(|variant| {
            if let Some(value) = variant
                .attrs
                .pop_if(|attr| attr.path().is_ident("code"))
                .as_ref()
                .map(Attribute::parse_args::<Expr>)
                .or(default.clone())
            {
                let variant = &variant.ident;
                let value = value?;
                Ok(quote! {
                    #enum_name::#variant => #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 enum_status_code::AsStatusCode for #enum_name {
                    fn as_status_code(&self) -> enum_status_code::http::StatusCode {
                        match self {
                            #contents
                        }
                    }
                }
            }
        })
        .ok_or(Error::new(enum_span, "expcted enum body"))
}