1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
use procmeta::prelude::*;
use syn::{DeriveInput, LitInt, LitStr};
#[derive(MetaParser)]
pub enum ParsedFieldAttr {
#[name("api_error")]
AssignApiError {
status: LitInt,
code: LitStr,
cause: LitStr,
},
// code, cause
#[name("api_error")]
BusinessError(LitStr, LitStr),
#[name("api_error")]
FromError(FromError),
}
#[derive(MetaParser)]
pub enum FromError {
#[name("from")]
Item(Expr),
#[name("from")]
AssignCauseItem(Expr, LitStr),
}
pub fn expand(input: &DeriveInput) -> Result<TokenStream> {
let data = &input.data;
let ty = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let mut collect_item_token = quote!();
let mut extend_token = quote!();
match data {
syn::Data::Struct(_) => unimplemented!(),
syn::Data::Enum(data) => {
for var in &data.variants {
let attrs = &var.attrs;
if attrs.is_empty() {
let fields = &var.fields;
if let syn::Fields::Unnamed(unnamed) = fields {
if unnamed.unnamed.len() == 1 {
let unnamed_ty = &unnamed.unnamed[0].ty;
extend_token = quote! {
#extend_token
result.extend(<#unnamed_ty as ApiErrors>::api_errors());
};
}
}
continue;
}
for attr in attrs {
let parsed_attr = ParsedFieldAttr::try_from(attr)?;
match parsed_attr {
ParsedFieldAttr::AssignApiError {
status,
code,
cause,
} => {
collect_item_token = quote! {
#collect_item_token
ApiErrorItem {
status: #status,
code: #code.into(),
cause: #cause.into(),
},
};
}
ParsedFieldAttr::BusinessError(code, cause) => {
collect_item_token = quote! {
#collect_item_token
ApiErrorItem {
status: 200,
code: #code.into(),
cause: #cause.into(),
},
};
}
ParsedFieldAttr::FromError(inner_err) => match inner_err {
FromError::Item(item) => {
collect_item_token = quote! {
#collect_item_token
#item.into(),
};
}
FromError::AssignCauseItem(item, cause) => {
collect_item_token = quote! {
#collect_item_token
{
let item: ApiErrorItem = #item.into();
ApiErrorItem {
status: item.status,
code: item.code,
cause: #cause.into(),
}
},
};
}
},
}
}
}
}
syn::Data::Union(_) => unimplemented!(),
}
let body_token = if extend_token.is_empty() {
quote!(vec![#collect_item_token])
} else {
quote! {
let mut result = vec![#collect_item_token];
#extend_token
result
}
};
let result = quote! {
impl #impl_generics ApiErrors for #ty #ty_generics #where_clause {
fn api_errors() -> Vec<ApiErrorItem> {
#body_token
}
}
};
Ok(result)
}