#![recursion_limit = "128"]
extern crate proc_macro;
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::{parse2, parse_quote, spanned::Spanned, Error, ItemEnum};
#[doc(hidden)]
#[proc_macro_attribute]
pub fn tyenum(_attr: proc_macro::TokenStream, item: proc_macro::TokenStream) -> proc_macro::TokenStream {
let item: TokenStream = item.into();
let mut tyenum: ItemEnum = parse2(item).unwrap();
let name = &tyenum.ident;
let mut impls = Vec::new();
for v in tyenum.variants.iter_mut() {
let ty: &dyn ToTokens;
let ident;
{
let mut iter = v.fields.iter();
ty = if let Some(f) = iter.next() {
if let Some(f) = iter.next() {
return Error::new(f.span(), "maximum one field in variants allowed").to_compile_error().into();
}
&f.ty
} else {
&v.ident
};
ident = &v.ident;
impls.push(quote! {
impl From<#ty> for #name {
fn from(variant: #ty) -> Self {
#name::#ident(variant)
}
}
impl TryFrom<#name> for #ty {
type Error = TryFromTyenumError;
fn try_from(e: #name) -> Result<Self, TryFromTyenumError> {
if let #name::#ident(variant) = e {
Ok(variant)
} else {
Err(TryFromTyenumError)
}
}
}
impl IsTypeOf<#name> for #ty {
fn is_type_of(e: &#name) -> bool {
if let #name::#ident(_) = e {
true
} else {
false
}
}
}
});
}
*v = parse_quote!(#ident(#ty));
}
quote!(
use std::convert::TryFrom;
use std::fmt;
use std::error::Error;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct TryFromTyenumError;
impl fmt::Display for TryFromTyenumError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "inner item is of a different type")
}
}
impl Error for TryFromTyenumError {}
trait IsTypeOf<E> {
fn is_type_of(e: &E) -> bool;
}
#tyenum
impl #name {
fn is<T: IsTypeOf<#name>>(&self) -> bool {
T::is_type_of(self)
}
}
#(#impls)*
)
.into()
}