enum_status_code_macros/
lib.rs1extern crate proc_macro;
2use itertools::Itertools;
3use manyhow::manyhow;
4use proc_macro2::TokenStream;
5use quote::quote;
6use syn::{Attribute, Error, Expr, ItemEnum, parse2, spanned::Spanned};
7
8#[manyhow(proc_macro_attribute)]
9pub fn status_code(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
10 let mut item: ItemEnum = parse2(item)?;
11 let enum_name = &item.ident;
12 let enum_span = item.span();
13
14 let default = (!attr.is_empty()).then(|| Ok(parse2(attr)?));
15 item.variants
16 .iter_mut()
17 .map(|variant| {
18 if let Some(value) = variant
19 .attrs
20 .pop_if(|attr| attr.path().is_ident("code"))
21 .as_ref()
22 .map(Attribute::parse_args::<Expr>)
23 .or(default.clone())
24 {
25 let variant = &variant.ident;
26 let value = value?;
27 Ok(quote! {
28 #enum_name::#variant => #value,
29 })
30 } else {
31 Err(Error::new(
32 variant.span(),
33 "variant is missing status code and no default set",
34 ))
35 }
36 })
37 .process_results(|iter| {
38 iter.tree_reduce(|a, b| {
39 quote! {
40 #a
41 #b
42 }
43 })
44 })?
45 .map(|contents| {
46 quote! {
47 #item
48
49 impl enum_status_code::AsStatusCode for #enum_name {
50 fn as_status_code(&self) -> enum_status_code::http::StatusCode {
51 match self {
52 #contents
53 }
54 }
55 }
56 }
57 })
58 .ok_or(Error::new(enum_span, "expcted enum body"))
59}