Skip to main content

rust_alert/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{DeriveInput, parse_macro_input};
4
5/// Creates an Alert struct that implements Display and From for multiple error types.
6///
7/// Usage:
8/// ```markdown
9/// #[alert(errors = [MyError, AnotherError])]
10/// pub struct Alert {
11///     message: String,
12/// }
13/// ```
14#[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    // Parse the error types from the attribute
21    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    // Generate the From implementations
35    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    // Generate the struct with derives and Display implementation
48    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}