ctxerr_derive 0.5.4

Tiny wrapper on thiserror embedding backtrace and location
Documentation
#![recursion_limit = "128"]
extern crate proc_macro;
extern crate proc_macro2;
use proc_macro2::Ident;
use syn::DeriveInput;

use quote::quote;

#[proc_macro_attribute]
pub fn ctxerr(
    _metadata: proc_macro::TokenStream,
    input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
    let ast: DeriveInput = syn::parse(input).unwrap();
    let name = ast.ident.to_string();
    if !name.ends_with("Kind") {
        panic!("Ttypename should ends with `Kind`!");
    }

    let name_ident = &ast.ident;
    let base_name = Ident::new(&name[0..name.len() - 4], ast.ident.span());
    let attrs = ast
        .attrs
        .iter()
        .cloned()
        .filter_map(|x| match x.meta {
            syn::Meta::List(list) => Some(list.tokens),
            _ => None,
        })
        .collect::<Vec<_>>();

    let tokens = quote! {
        pub use __private::*;

        mod __private {
            use super::*;
            use ctxerr::thiserror;

            #[derive(Debug, ctxerr::thiserror::Error)]
            #ast

            #[derive(ctxerr::thiserror::Error #(, #attrs)*)]
            pub struct #base_name {
                pub kind: #name_ident,
                pub backtrace: Option<std::backtrace::Backtrace>,
                pub location: Option<&'static core::panic::Location<'static>>,
            }

            impl std::fmt::Display for #base_name {
                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                    write!(f, "{}", self.kind)?;
                    if let Some(loc) = self.location {
                        writeln!(f, " in file: {}:{}:{}", loc.file(), loc.line(), loc.column())?;
                    } else {
                        writeln!(f)?;
                    }

                    Ok(())
                }
            }

            impl std::fmt::Debug for #base_name {
                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                    write!(f, "{}", &self)?;
                    if let Some(b) = &self.backtrace {
                        writeln!(f, "backtrace: \n{}", b)?;
                    }

                    Ok(())
                }
            }


            impl<T: Into<ErrorKind>> From<T> for #base_name {
                #[track_caller]
                fn from(err: T) -> Self {
                    let b = std::backtrace::Backtrace::capture();
                    Self{
                        kind: err.into(),
                        backtrace: Some(std::backtrace::Backtrace::capture()),
                        location: Some(core::panic::Location::caller()),
                    }
                }
            }
        }

    };

    tokens.into()
}