1use darling::{FromMeta, ast::NestedMeta};
7use quote::quote;
8use syn::Data;
9
10#[derive(FromMeta)]
12struct ApErrorArgs {
13 #[darling(flatten)]
14 #[allow(dead_code)]
15 error_type: ApErrorType,
16}
17
18#[derive(FromMeta)]
19#[darling(rename_all = "snake_case")]
20enum ApErrorType {
21 Flat,
23}
24
25#[proc_macro_attribute]
43pub fn ap_error(
44 args: proc_macro::TokenStream,
45 item: proc_macro::TokenStream,
46) -> proc_macro::TokenStream {
47 let attr_args = match NestedMeta::parse_meta_list(args.into()) {
48 Ok(v) => v,
49 Err(e) => {
50 return proc_macro::TokenStream::from(darling::Error::from(e).write_errors());
51 }
52 };
53
54 let _args = match ApErrorArgs::from_list(&attr_args) {
55 Ok(params) => params,
56 Err(error) => {
57 return proc_macro::TokenStream::from(error.write_errors());
58 }
59 };
60
61 let input = syn::parse_macro_input!(item as syn::DeriveInput);
62 let type_identifier = &input.ident;
63
64 ap_error_flat(&input, type_identifier)
66}
67
68fn ap_error_flat(
69 input: &syn::DeriveInput,
70 type_identifier: &proc_macro2::Ident,
71) -> proc_macro::TokenStream {
72 match &input.data {
73 Data::Enum(data) => {
74 let match_arms = data.variants.iter().map(|variant| {
75 let variant_ident = &variant.ident;
76 let variant_str = variant_ident.to_string();
77
78 match variant.fields {
79 syn::Fields::Unit => {
80 quote! {
81 #type_identifier::#variant_ident => #variant_str
82 }
83 }
84 syn::Fields::Named(_) => {
85 quote! {
86 #type_identifier::#variant_ident { .. } => #variant_str
87 }
88 }
89 syn::Fields::Unnamed(_) => {
90 quote! {
91 #type_identifier::#variant_ident(..) => #variant_str
92 }
93 }
94 }
95 });
96
97 quote! {
98 #input
99
100 #[automatically_derived]
101 impl ::ap_error::flat_error::FlatError for #type_identifier {
102 fn error_variant(&self) -> &'static str {
103 match &self {
104 #(#match_arms), *
105 }
106 }
107 }
108 }
109 .into()
110 }
111 _ => syn::Error::new_spanned(input, "ap_error can only be used with enums")
112 .to_compile_error()
113 .into(),
114 }
115}