Skip to main content

enum_status_code_macros/
lib.rs

1extern 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}