apidoc_expand/
api_error.rs1use procmeta::prelude::*;
2use syn::{DeriveInput, LitInt, LitStr};
3
4#[derive(MetaParser)]
5pub enum ParsedErrorFieldAttr {
6 #[name("error")]
7 Error(LitStr),
8}
9
10#[derive(MetaParser)]
11pub enum ParsedApiFieldAttr {
12 #[name("api_error")]
13 AssignApiError {
14 status: LitInt,
15 code: LitStr,
16 cause: LitStr,
17 },
18
19 #[name("api_error")]
21 BusinessCodeError(LitStr),
22
23 #[name("api_error")]
25 BusinessCodeCauseError(LitStr, LitStr),
26
27 #[name("api_error")]
28 FromError(FromError),
29}
30
31#[derive(MetaParser)]
32pub enum FromError {
33 #[name("from")]
34 Item(Expr),
35
36 #[name("from")]
37 AssignCauseItem(Expr, LitStr),
38}
39
40pub fn expand(input: &DeriveInput) -> Result<TokenStream> {
41 let data = &input.data;
42 let ty = &input.ident;
43 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
44
45 let mut collect_item_token = quote!();
46 let mut extend_token = quote!();
47 match data {
48 syn::Data::Struct(_) => unimplemented!(),
49 syn::Data::Enum(data) => {
50 for var in &data.variants {
51 let mut this_error: Option<ParsedErrorFieldAttr> = None;
52 let mut api_error: Option<ParsedApiFieldAttr> = None;
53 let attrs = &var.attrs;
54 for attr in attrs {
55 let parsed_attr = ParsedErrorFieldAttr::try_from(attr);
56 if let Ok(parsed_attr) = parsed_attr {
57 this_error = Some(parsed_attr);
58 continue;
59 }
60
61 let parsed_attr = ParsedApiFieldAttr::try_from(attr)?;
62 if api_error.is_some() {
63 return Err(Error::new(Span::call_site(), "dumplicate api_error"));
64 }
65 api_error = Some(parsed_attr);
66 }
67 if let Some(parsed_attr) = api_error {
68 match parsed_attr {
69 ParsedApiFieldAttr::AssignApiError {
70 status,
71 code,
72 cause,
73 } => {
74 collect_item_token = quote! {
75 #collect_item_token
76 ApiErrorItem {
77 status: #status,
78 code: #code.into(),
79 cause: #cause.into(),
80 },
81 };
82 }
83 ParsedApiFieldAttr::BusinessCodeCauseError(code, cause) => {
84 collect_item_token = quote! {
85 #collect_item_token
86 ApiErrorItem {
87 status: 200,
88 code: #code.into(),
89 cause: #cause.into(),
90 },
91 };
92 }
93 ParsedApiFieldAttr::FromError(inner_err) => match inner_err {
94 FromError::Item(item) => {
95 collect_item_token = quote! {
96 #collect_item_token
97 #item.into(),
98 };
99 }
100 FromError::AssignCauseItem(item, cause) => {
101 collect_item_token = quote! {
102 #collect_item_token
103 {
104 let item: ApiErrorItem = #item.into();
105 ApiErrorItem {
106 status: item.status,
107 code: item.code,
108 cause: #cause.into(),
109 }
110 },
111 };
112 }
113 },
114 ParsedApiFieldAttr::BusinessCodeError(code) => {
115 let cause = this_error
116 .ok_or(Error::new(Span::call_site(), "api_error miss cause"))?;
117 let ParsedErrorFieldAttr::Error(cause) = cause;
118 collect_item_token = quote! {
119 #collect_item_token
120 ApiErrorItem {
121 status: 200,
122 code: #code.into(),
123 cause: #cause.into(),
124 },
125 };
126 }
127 }
128 } else {
129 let fields = &var.fields;
130 if let syn::Fields::Unnamed(unnamed) = fields {
131 if unnamed.unnamed.len() == 1 {
132 let unnamed_ty = &unnamed.unnamed[0].ty;
133
134 extend_token = quote! {
135 #extend_token
136 result.extend(<#unnamed_ty as ApiErrors>::api_errors());
137 };
138 }
139 }
140 }
141 }
142 }
143 syn::Data::Union(_) => unimplemented!(),
144 }
145 let body_token = if extend_token.is_empty() {
146 quote!(vec![#collect_item_token])
147 } else {
148 quote! {
149 let mut result = vec![#collect_item_token];
150 #extend_token
151 result
152 }
153 };
154 let result = quote! {
155 impl #impl_generics ApiErrors for #ty #ty_generics #where_clause {
156 fn api_errors() -> Vec<ApiErrorItem> {
157 #body_token
158 }
159 }
160 };
161 Ok(result)
162}