snafu_virtstack_macro/
lib.rs1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{Data, DeriveInput, parse_macro_input};
4
5#[proc_macro_attribute]
16pub fn stack_trace_debug(_args: TokenStream, input: TokenStream) -> TokenStream {
17 let input = parse_macro_input!(input as DeriveInput);
18
19 match generate_stack_trace_impl(&input) {
21 Ok(tokens) => tokens.into(),
22 Err(err) => err.to_compile_error().into(),
23 }
24}
25
26fn generate_stack_trace_impl(input: &DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
27 let name = &input.ident;
28 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
29
30 let _data = match &input.data {
32 Data::Enum(data) => data,
33 _ => {
34 return Err(syn::Error::new_spanned(
35 input,
36 "stack_trace_debug can only be applied to enums",
37 ));
38 }
39 };
40
41 let stack_trace_impl =
43 generate_virtual_stack_trace_impl(name, &impl_generics, &ty_generics, where_clause)?;
44
45 Ok(quote! {
46 #input
48
49 #stack_trace_impl
51 })
52}
53
54fn generate_virtual_stack_trace_impl(
55 name: &syn::Ident,
56 impl_generics: &syn::ImplGenerics,
57 ty_generics: &syn::TypeGenerics,
58 where_clause: Option<&syn::WhereClause>,
59) -> syn::Result<proc_macro2::TokenStream> {
60 Ok(quote! {
61 impl #impl_generics snafu_virtstack::VirtualStackTrace for #name #ty_generics #where_clause {
62 #[track_caller]
63 fn virtual_stack(&self) -> Vec<snafu_virtstack::StackFrame> {
64 let mut stack = vec![snafu_virtstack::StackFrame::new(
65 std::panic::Location::caller(),
66 self.to_string(),
67 )];
68
69 let mut current_error = self as &dyn std::error::Error;
71 while let Some(source) = current_error.source() {
72 stack.push(snafu_virtstack::StackFrame::new(
74 std::panic::Location::caller(),
75 source.to_string(),
76 ));
77 current_error = source;
78 }
79
80 stack
81 }
82 }
83
84 impl #impl_generics std::fmt::Debug for #name #ty_generics #where_clause {
85 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86 use snafu_virtstack::VirtualStackTrace;
87
88 writeln!(f, "Error: {}", self)?;
89 writeln!(f, "Virtual Stack Trace:")?;
90
91 let stack = self.virtual_stack();
92 for (i, frame) in stack.iter().enumerate() {
93 writeln!(f, " {}: {}", i, frame)?;
94 }
95
96 Ok(())
97 }
98 }
99 })
100}