#![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()
}