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"))
}