1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{DeriveInput, parse_macro_input};
4
5#[proc_macro_attribute]
15pub fn alert(args: TokenStream, input: TokenStream) -> TokenStream {
16 let input = parse_macro_input!(input as DeriveInput);
17 let struct_name = &input.ident;
18 let struct_vis = &input.vis;
19
20 let attr_parser = parse_macro_input!(args as syn::MetaNameValue);
22 let error_types = match attr_parser.value {
23 syn::Expr::Array(array) => array
24 .elems
25 .iter()
26 .map(|expr| match expr {
27 syn::Expr::Path(path) => path.path.clone(),
28 _ => panic!("Expected type paths in errors list"),
29 })
30 .collect::<Vec<_>>(),
31 _ => panic!("Expected array syntax: errors = [Type1, Type2, ...]"),
32 };
33
34 let from_impls = error_types.iter().map(|error_type| {
36 quote! {
37 impl From<#error_type> for #struct_name {
38 fn from(value: #error_type) -> Self {
39 #struct_name {
40 message: format!("{}: {}", stringify!(#error_type), value),
41 }
42 }
43 }
44 }
45 });
46
47 let expanded = quote! {
49 #[derive(Debug, Clone, PartialEq)]
50 #struct_vis struct #struct_name {
51 message: String,
52 }
53
54 impl std::fmt::Display for #struct_name {
55 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56 writeln!(f, "{}", self.message)
57 }
58 }
59
60 #(#from_impls)*
61 };
62
63 TokenStream::from(expanded)
64}