#![recursion_limit = "128"]
extern crate proc_macro;
#[macro_use]
extern crate quote;
#[macro_use]
extern crate syn;
use proc_macro::TokenStream;
use syn::DeriveInput;
#[doc(hidden)]
#[proc_macro_derive(TrackableError, attributes(trackable))]
pub fn derive_trackable_error(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let expanded = impl_trackable_error(&ast);
expanded.into()
}
fn impl_trackable_error(ast: &syn::DeriveInput) -> impl Into<TokenStream> {
let error = &ast.ident;
let error_kind = get_error_kind(&ast.attrs);
quote! {
impl ::std::ops::Deref for #error {
type Target = ::trackable::error::TrackableError<#error_kind>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl ::std::fmt::Display for #error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
self.0.fmt(f)
}
}
impl ::std::error::Error for #error {
fn source(&self) -> Option<&(::std::error::Error + 'static)> {
self.0.source()
}
}
impl ::trackable::Trackable for #error {
type Event = ::trackable::Location;
#[inline]
fn history(&self) -> Option<&::trackable::History<Self::Event>> {
self.0.history()
}
#[inline]
fn history_mut(&mut self) -> Option<&mut ::trackable::History<Self::Event>> {
self.0.history_mut()
}
}
impl From<::trackable::error::TrackableError<#error_kind>> for #error {
#[inline]
fn from(f: ::trackable::error::TrackableError<#error_kind>) -> Self {
#error(f)
}
}
impl From<#error> for ::trackable::error::TrackableError<#error_kind> {
#[inline]
fn from(f: #error) -> Self {
f.0
}
}
impl From<#error_kind> for #error {
#[inline]
fn from(f: #error_kind) -> Self {
use ::trackable::error::ErrorKindExt;
f.error().into()
}
}
}
}
fn get_error_kind(attrs: &[syn::Attribute]) -> syn::Path {
use syn::Lit::*;
use syn::Meta::*;
use syn::MetaNameValue;
use syn::NestedMeta::*;
let mut error_kind = "ErrorKind".to_owned();
let attrs = attrs
.iter()
.filter_map(|attr| {
let path = &attr.path;
if quote!(#path).to_string() == "trackable" {
Some(attr.parse_meta().unwrap_or_else(|e| {
panic!("invalid trackable syntax: {} (error={})", quote!(attr), e)
}))
} else {
None
}
})
.flat_map(|m| match m {
List(l) => l.nested,
tokens => panic!("unsupported syntax: {}", quote!(#tokens).to_string()),
})
.map(|m| match m {
Meta(m) => m,
tokens => panic!("unsupported syntax: {}", quote!(#tokens).to_string()),
});
for attr in attrs {
match &attr {
NameValue(MetaNameValue {
path,
lit: Str(value),
..
}) if path
.get_ident()
.map_or(false, |ident| ident == "error_kind") =>
{
error_kind = value.value().to_string();
}
i @ List(..) | i @ Path(..) | i @ NameValue(..) => {
panic!("unsupported option: {}", quote!(#i))
}
}
}
match syn::parse_str(&error_kind) {
Err(e) => panic!("{:?} is not a valid type (parse error: {})", error_kind, e),
Ok(path) => path,
}
}