extern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro_derive(Primitive)]
pub fn primitive(input: TokenStream) -> TokenStream {
let ast = syn::parse_macro_input!(input as syn::DeriveInput);
impl_primitive(&ast)
}
fn impl_primitive(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
if let syn::Data::Enum(ref variant) = ast.data {
let (var_u64, dis_u64): (Vec<_>, Vec<_>) = variant
.variants
.iter()
.map(|v| {
match v.fields {
syn::Fields::Unit => (),
_ => panic!("#[derive(Primitive) can only operate on C-like enums"),
}
if v.discriminant.is_none() {
panic!(
"#[derive(Primitive) requires C-like enums with \
discriminants for all enum variants"
);
}
let discrim = match v.discriminant.clone().map(|(_eq, expr)| expr).unwrap() {
syn::Expr::Cast(real) => *real.expr,
orig => orig,
};
(v.ident.clone(), discrim)
})
.unzip();
let enum_u64 = vec![name.clone(); variant.variants.len()];
let var_i64 = var_u64.clone();
let dis_i64 = dis_u64.clone();
let enum_i64 = enum_u64.clone();
let to_name = name.clone();
let to_enum_u64 = enum_u64.clone();
let to_var_u64 = var_u64.clone();
let to_dis_u64 = dis_u64.clone();
let to_enum_i64 = enum_u64.clone();
let to_var_i64 = var_u64.clone();
let to_dis_i64 = dis_u64.clone();
TokenStream::from(quote::quote! {
impl ::num_traits::FromPrimitive for #name {
fn from_u64(val: u64) -> Option<Self> {
match val as _ {
#( #dis_u64 => Some(#enum_u64::#var_u64), )*
_ => None,
}
}
fn from_i64(val: i64) -> Option<Self> {
match val as _ {
#( #dis_i64 => Some(#enum_i64::#var_i64), )*
_ => None,
}
}
}
impl ::num_traits::ToPrimitive for #to_name {
fn to_u64(&self) -> Option<u64> {
match *self {
#( #to_enum_u64::#to_var_u64 => Some(#to_dis_u64 as u64), )*
}
}
fn to_i64(&self) -> Option<i64> {
match *self {
#( #to_enum_i64::#to_var_i64 => Some(#to_dis_i64 as i64), )*
}
}
}
impl ::core::convert::TryFrom<u64> for #to_name {
type Error = &'static str;
fn try_from(value: u64) -> ::core::result::Result<Self, <Self as ::core::convert::TryFrom<u64>>::Error> {
use ::num_traits::FromPrimitive;
#to_name::from_u64(value).ok_or_else(|| "Unknown variant")
}
}
impl ::core::convert::TryFrom<u32> for #to_name {
type Error = &'static str;
fn try_from(value: u32) -> ::core::result::Result<Self, <Self as ::core::convert::TryFrom<u32>>::Error> {
use ::num_traits::FromPrimitive;
#to_name::from_u32(value).ok_or_else(|| "Unknown variant")
}
}
impl ::core::convert::TryFrom<u16> for #to_name {
type Error = &'static str;
fn try_from(value: u16) -> ::core::result::Result<Self, <Self as ::core::convert::TryFrom<u16>>::Error> {
use ::num_traits::FromPrimitive;
#to_name::from_u16(value).ok_or_else(|| "Unknown variant")
}
}
impl ::core::convert::TryFrom<u8> for #to_name {
type Error = &'static str;
fn try_from(value: u8) -> ::core::result::Result<Self, <Self as ::core::convert::TryFrom<u8>>::Error> {
use ::num_traits::FromPrimitive;
#to_name::from_u8(value).ok_or_else(|| "Unknown variant")
}
}
impl ::core::convert::TryFrom<i64> for #name {
type Error = &'static str;
fn try_from(value: i64) -> ::core::result::Result<Self, <Self as ::core::convert::TryFrom<i64>>::Error> {
use ::num_traits::FromPrimitive;
#to_name::from_i64(value).ok_or_else(|| "Unknown variant")
}
}
impl ::core::convert::TryFrom<i32> for #name {
type Error = &'static str;
fn try_from(value: i32) -> ::core::result::Result<Self, <Self as ::core::convert::TryFrom<i32>>::Error> {
use ::num_traits::FromPrimitive;
#to_name::from_i32(value).ok_or_else(|| "Unknown variant")
}
}
impl ::core::convert::TryFrom<i16> for #name {
type Error = &'static str;
fn try_from(value: i16) -> ::core::result::Result<Self, <Self as ::core::convert::TryFrom<i16>>::Error> {
use ::num_traits::FromPrimitive;
#to_name::from_i16(value).ok_or_else(|| "Unknown variant")
}
}
impl ::core::convert::TryFrom<i8> for #name {
type Error = &'static str;
fn try_from(value: i8) -> ::core::result::Result<Self, <Self as ::core::convert::TryFrom<i8>>::Error> {
use ::num_traits::FromPrimitive;
#to_name::from_i8(value).ok_or_else(|| "Unknown variant")
}
}
})
} else {
panic!("#[derive(Primitive)] is only valid for C-like enums");
}
}